Compare commits

..

856 Commits

Author SHA1 Message Date
Theodore Li
74fe25cacc Fix panel logic 2026-03-14 18:53:16 -07:00
Theodore Li
5e95d33705 Remove console log 2026-03-14 18:08:23 -07:00
Theodore Li
20a626573d Lint fix 2026-03-14 18:07:02 -07:00
Theodore Li
15429244f1 Improve rerendering of resource view 2026-03-14 17:54:18 -07:00
Siddharth Ganesan
f077751ce8 fix(mothership): file materialization tools (#3586)
* Fix ope

* File upload fixes

* Fix lint

* Materialization shows up

* Snapshot

* Fix

* Nuke migrations

* Add migs

* migs

---------

Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
2026-03-14 16:56:44 -07:00
Vikhyath Mondreti
75bdf46e6b improvement(promos): promo codes should be only stripe codes (#3591)
* improvement(promos): promo codes should be only stripe codes

* address comments
2026-03-14 16:28:18 -07:00
Waleed
952915abfc fix(sidebar): collapsed sidebar shows single icons with hover dropdown menus (#3588)
* fix(sidebar): collapsed sidebar shows single icons with hover dropdown menus

* fix(sidebar): truncate long names in collapsed dropdown menus

* fix(sidebar): address PR review — extract components, fix reactive subscription

* fix(sidebar): support touch/keyboard for collapsed menus, document auto-collapse

* fix(sidebar): remove dead CSS selector for sidebar-collapse-remove

* fix(sidebar): add aria-label to collapsed menu trigger buttons

* fix(sidebar): use useLayoutEffect for attribute removal, remove dead branch
2026-03-14 15:21:41 -07:00
Waleed
cbc9f4248c improvement(cleanup): remove unused old ui components (#3589) 2026-03-14 15:08:44 -07:00
Theodore Li
5ba3118495 feat(byok-migration) byok migration script (#3584)
* Add byok migration script

* Fix lint

* Add skipping if byok already provided

* Fix lint

---------

Co-authored-by: Theodore Li <theo@sim.ai>
2026-03-14 16:11:21 -04:00
Theodore Li
00ff21ab9c fix(workflow) Fix embedded workflow logs (#3587)
* Extract workflow run logic into shared util

* Fix lint

* Fix isRunning being stuck in true

* Fix lint

---------

Co-authored-by: Theodore Li <theo@sim.ai>
2026-03-14 16:05:53 -04:00
Vikhyath Mondreti
a2f8ed06c8 fix lint 2026-03-14 12:12:36 -07:00
Theodore Li
f347e3fca0 fix(firecrawl) fix firecrawl scrape credit usage calculation (#3583)
* fix(firecrawl) fix firecrawl scrape credit usage calculation

* Update apps/sim/tools/firecrawl/scrape.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Fix syntax

---------

Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-14 14:27:22 -04:00
PlaneInABottle
e13f52fea2 fix(tools): support stringified HTTP request tables (#3565)
* fix(tools): support stringified HTTP request tables

Accept stored header and query tables after they are reloaded from UI JSON so HTTP requests keep their query strings and URL-encoded body handling intact.

* test: mock AuthType in async execute route

* test(tools): cover invalid stringified HTTP inputs

---------

Co-authored-by: test <test@example.com>
2026-03-14 11:20:11 -07:00
PlaneInABottle
e6b2b739cf fix(execution): report cancellation durability truthfully (#3550)
* fix(cancel): report cancellation durability truthfully

Return explicit durability results for execution cancellation so success only reflects persisted cancellation state instead of best-effort Redis availability.

* fix: hoist cancellation test mocks

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(sim): harden execution cancel durability

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(sim): fallback manual cancel without redis

Abort active manual SSE executions locally when Redis cannot durably record the cancellation marker so the run still finalizes as cancelled instead of completing normally.

* test: mock AuthType in async execute route

Keep the rebased async execute route test aligned with the current hybrid auth module exports so it exercises the queueing path instead of failing at import time.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: test <test@example.com>
2026-03-14 11:18:15 -07:00
Vikhyath Mondreti
9ae656c0d5 fix(files): default file name (#3585) 2026-03-14 11:15:00 -07:00
Vikhyath Mondreti
c738226c06 fix(file): bun issues with new file creation (#3582)
* fix(files): new file bun error

* update constant

* fix types
2026-03-14 10:56:13 -07:00
Waleed
8f15be23a0 fix(ashby): add secretToken to webhook creation and fix trigger UX (#3580)
* fix(ashby): add secretToken to webhook creation and fix trigger UX

* fix(toast): restore useMemo for context value to prevent unnecessary re-renders

* fix(notifications): track exit animation timeout so pauseAll can cancel it

* fix(notifications): use isPausedRef to guard exit timeout instead of synthetic timer keys

* fix(notifications): clear pending timers when notification stack empties
2026-03-14 06:26:06 -07:00
Waleed
b2d146ca0a improvement(mothership): message queueing for home chat (#3576)
* improvement(mothership): message queueing for home chat

* fix(mothership): address PR review — move FileAttachmentForApi to types, defer onEditValueConsumed to effect, await sendMessage in sendNow

* fix(mothership): replace updater side-effect with useEffect ref sync, move sendMessageRef to useLayoutEffect

* fix(mothership): clear message queue on chat switch while sending

* fix(mothership): remove stale isSending from handleKeyDown deps

* fix(mothership): guard sendNow against double-click duplicate sends

* fix(mothership): simplify queue callbacks — drop redundant deps and guard ref

- Remove `setMessageQueue` from useCallback deps (stable setter, never changes)
- Replace `sendNowProcessingRef` double-click guard with eager `messageQueueRef` update
- Simplify `editQueuedMessage` with same eager-ref pattern for consistency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(mothership): clear edit value on nav, stop queue drain on send failure

- Reset editingInputValue when chatId changes so stale edit text
  doesn't leak into the next chat
- Pass error flag to finalize so queue is cleared (not drained) when
  sendMessage fails — prevents cascading failures on auth expiry or
  rate limiting from silently consuming every queued message

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(mothership): eagerly update messageQueueRef in removeFromQueue

Match the pattern used by sendNow and editQueuedMessage — update the
ref synchronously so finalize's microtask cannot read a stale queue
and drain a message the user just removed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(mothership): mark onSendNow as explicit fire-and-forget

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 06:24:00 -07:00
Waleed
d06aa1de7e fix(connectors): align connector scopes with oauth config and fix kb modal UX (#3573)
* fix(connectors): align connector scopes with oauth config and fix kb modal UX

* fix(connectors): restore onCheckedChange for keyboard accessibility

* feat(connectors): add dynamic selectors to knowledge base connector config

Replace manual ID text inputs with dynamic selector dropdowns that fetch
options from the existing selector registry. Users can toggle between
selector and manual input via canonical pairs (basic/advanced mode).

Adds selector support to 12 connectors: Airtable (cascading base→table),
Slack, Gmail, Google Calendar, Linear (cascading team→project), Jira,
Confluence, MS Teams (cascading team→channel), Notion, Asana, Webflow,
and Outlook. Dependency clearing propagates across canonical siblings to
prevent stale cross-mode data on submit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* updated animated blocks UI

* fix(connectors): clear canonical siblings of dependents and resolve active mode values

Fixes three issues from PR review:
- Dependency clearing now includes canonical siblings of dependent fields
  (e.g., changing base clears both tableSelector AND tableIdOrName)
- Selector context and depsResolved now resolve dependency values through
  the active canonical mode, not just the raw depFieldId
- Tooltip text changed from "Switch to manual ID" to "Switch to manual input"
  to correctly describe dropdown fallbacks (e.g., Outlook folder)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: linter class ordering fixes and docs link update

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(connectors): reset apiKeyFocused on connector re-selection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 03:31:07 -07:00
Siddharth Ganesan
5b9f0d73c2 feat(mothership): mothership (#3411)
* Fix lint

* improvement(sidebar): loading

* fix(sidebar): use client-generated UUIDs for stable optimistic updates (#3439)

* fix(sidebar): use client-generated UUIDs for stable optimistic updates

* fix(folders): use zod schema validation for folder create API

Replace inline UUID regex with zod schema validation for consistency
with other API routes. Update test expectations accordingly.

* fix(sidebar): add client UUID to single workflow duplicate hook

The useDuplicateWorkflow hook was missing newId: crypto.randomUUID(),
causing the same temp-ID-swap issue for single workflow duplication
from the context menu.

* fix(folders): avoid unnecessary Set re-creation in replaceOptimisticEntry

Only create new expandedFolders/selectedFolders Sets when tempId
differs from data.id. In the common happy path (client-generated UUIDs),
this avoids unnecessary Zustand state reference changes and re-renders.

* Mothership block logs

* Fix mothership block logs

* improvement(knowledge): make connector-synced document chunks readonly (#3440)

* improvement(knowledge): make connector-synced document chunks readonly

* fix(knowledge): enforce connector chunk readonly on server side

* fix(knowledge): disable toggle and delete actions for connector-synced chunks

* Job exeuction logs

* Job logs

* fix(connectors): remove unverifiable requiredScopes for Linear connector

* fix(connectors): remove legacy requiredScopes from Jira and Confluence connectors

Jira and Confluence OAuth tokens don't return legacy scope names like
read:jira-work or read:confluence-content.all, causing the 'Update access'
banner to always appear. Set requiredScopes to empty array like Linear.

* feat(tasks): add rename to task context menu (#3442)

* Revert "fix(connectors): remove legacy requiredScopes from Jira and Confluence connectors"

This reverts commit a0be3ff414.

* fix(connectors): restore Linear connector requiredScopes

Linear OAuth does return scopes in the token response. The previous
fix of emptying requiredScopes was based on an incorrect assumption.
Restoring requiredScopes: ['read'] as it should work correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(knowledge): pass workspaceId to useOAuthCredentials in connector card

The ConnectorCard was calling useOAuthCredentials(providerId) without
a workspaceId, causing the credentials API to return an empty array.
This meant the credential lookup always failed, getMissingRequiredScopes
received undefined, and the "Update access" banner always appeared.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix oauth link callback from mothership task

* feat(connectors): add Fireflies connector and API key auth support (#3448)

* feat(connectors): add Fireflies connector and API key auth support

Extend the connector system to support both OAuth and API key authentication
via a discriminated union (`ConnectorAuthConfig`). Add Fireflies as the first
API key connector, syncing meeting transcripts via the Fireflies GraphQL API.

Schema changes:
- Make `credentialId` nullable (null for API key connectors)
- Add `encryptedApiKey` column (AES-256-GCM encrypted, null for OAuth)

This eliminates the `'_apikey_'` sentinel and inline `sourceConfig._encryptedApiKey`
patterns, giving each auth mode its own clean column.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(fireflies): allow 0 for maxTranscripts (means unlimited)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Add context

* fix(fireflies): correct types from live API validation (#3450)

* fix(fireflies): correct types from live API validation

- speakers.id is number, not string (API returns 0, 1, 2...)
- summary.action_items is a single string, not string[]
- Update formatTranscriptContent to handle action_items as string

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(fireflies): correct tool types from live API validation

- FirefliesSpeaker.id: string -> number
- FirefliesSentence.speaker_id: string -> number
- FirefliesSpeakerAnalytics.speaker_id: string -> number
- FirefliesSummary.action_items: string[] -> string
- FirefliesSummary.outline: string[] -> string
- FirefliesSummary.shorthand_bullet: string[] -> string
- FirefliesSummary.bullet_gist: string[] -> string
- FirefliesSummary.topics_discussed: string[] -> string

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat(knowledge): add connector tools and expand document metadata (#3452)

* feat(knowledge): add connector tools and expand document metadata

* fix(knowledge): address PR review feedback on new tools

* fix(knowledge): remove unused params from get_document transform

* refactor, improvement

* fix: correct knowledge block canonical pair pattern and subblock migration

- Rename manualDocumentId to documentId (advanced subblock ID should match
  canonicalParamId, consistent with airtable/gmail patterns)
- Fix documentSelector.dependsOn to reference knowledgeBaseSelector (basic
  depends on basic, not advanced)
- Remove unnecessary documentId migration (ID unchanged from main)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* lint

* fix: resolve post-merge test and lint failures

- airtable: sync tableSelector condition with tableId (add getSchema)
- backfillCanonicalModes test: add documentId mode to prevent false backfill
- schedule PUT test: use invalid action string now that disable is valid
- schedule execute tests: add ne mock, sourceType field, use
  mockReturnValueOnce for two db.update calls
- knowledge tools: fix biome formatting (single-line arrow functions)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fixes

* Fixes

* Clean vfs

* Fix

* Fix lint

* fix(connectors): add rate limiting, concurrency controls, and bug fixes (#3457)

* fix(connectors): add rate limiting, concurrency controls, and bug fixes across knowledge connectors

- Add Retry-After header support to fetchWithRetry for all 18 connectors
- Batch concurrent API calls (concurrency 5) in Dropbox, Google Docs, Google Drive, OneDrive, SharePoint
- Batch concurrent API calls (concurrency 3) in Notion to match 3 req/s limit
- Cache GitHub tree in syncContext to avoid re-fetching on every pagination page
- Batch GitHub blob fetches with concurrency 5
- Fix GitHub base64 decoding: atob() → Buffer.from() for UTF-8 safety
- Fix HubSpot OAuth scope: 'tickets' → 'crm.objects.tickets.read' (v3 API)
- Fix HubSpot syncContext key: totalFetched → totalDocsFetched for consistency
- Add jitter to nextSyncAt (10% of interval, capped at 5min) to prevent thundering herd
- Fix Date consistency in connector DELETE route

* fix(connectors): address PR review feedback on retry and SharePoint batching

- Remove 120s cap on Retry-After — pass all values through to retry loop
- Add maxDelayMs guard: if Retry-After exceeds maxDelayMs, throw immediately
  instead of hammering with shorter intervals (addresses validate timeout concern)
- Add early exit in SharePoint batch loop when maxFiles limit is reached
  to avoid unnecessary API calls

* fix(connectors): cap Retry-After at maxDelayMs instead of aborting

Match Google Cloud SDK behavior: when Retry-After exceeds maxDelayMs,
cap the wait to maxDelayMs and log a warning, rather than throwing
immediately. This ensures retries are bounded in duration while still
respecting server guidance within the configured limit.

* fix(connectors): add early-exit guard to Dropbox, Google Docs, OneDrive batch loops

Match the SharePoint fix — skip remaining batches once maxFiles limit
is reached to avoid unnecessary API calls.

* improvement(turbo): align turborepo config with best practices (#3458)

* improvement(turbo): align turborepo config with best practices

* fix(turbo): address PR review feedback

* fix(turbo): add lint:check task for read-only lint+format CI checks

lint:check previously delegated to format:check which only checked
formatting. Now it runs biome check (no --write) which enforces both
lint rules and formatting without mutating files.

* upgrade turbo

* improvement(perf): apply react and js performance optimizations across codebase (#3459)

* improvement(perf): apply react and js performance optimizations across codebase

- Parallelize independent DB queries with Promise.all in API routes
- Defer PostHog and OneDollarStats via dynamic import() to reduce bundle size
- Use functional setState in countdown timers to prevent stale closures
- Replace O(n*m) .filter().find() with Set-based O(n) lookups in undo-redo
- Use .toSorted() instead of .sort() for immutable state operations
- Use lazy initializers for useState(new Set()) across 20 components
- Remove useMemo wrapping trivially cheap expressions (typeof, ternary, template strings)
- Add passive: true to scroll event listener

* fix(perf): address PR review feedback

- Extract IIFE Set patterns to named consts for readability in use-undo-redo
- Hoist Set construction above loops in BATCH_UPDATE_PARENT cases
- Add .catch() error handler to PostHog dynamic import
- Convert session-provider posthog import to dynamic import() to complete bundle split

* fix(analytics): add .catch() to onedollarstats dynamic import

* improvement(resource): tables, files

* improvement(resources): all outer page structure complete

* refactor(queries): comprehensive TanStack Query best practices audit (#3460)

* refactor: comprehensive TanStack Query best practices audit and migration

- Add AbortSignal forwarding to all 41 queryFn implementations for proper request cancellation
- Migrate manual fetch patterns to useMutation hooks (useResetPassword, useRedeemReferralCode, usePurchaseCredits, useImportWorkflow, useOpenBillingPortal, useAllowedMcpDomains)
- Migrate standalone hooks to TanStack Query (use-next-available-slot, use-mcp-server-test, use-webhook-management, use-referral-attribution)
- Fix query key factories: add missing `all` keys, replace inline keys with factory methods
- Fix optimistic mutations: use onSettled instead of onSuccess for cache reconciliation
- Replace overly broad cache invalidations with targeted key invalidation
- Remove keepPreviousData from static-key queries where it provides no benefit
- Add staleTime to queries missing explicit cache duration
- Fix `any` type in UpdateSettingParams with proper GeneralSettings typing
- Remove dead code: loadingWebhooks/checkedWebhooks from subblock store, unused helper functions
- Update settings components (general, debug, referral-code, credit-balance, subscription, mcp) to use mutation state instead of manual useState for loading/error/success

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove unstable mutation object from useCallback deps

openBillingPortal mutation object is not referentially stable,
but .mutate() is stable in TanStack Query v5. Remove from deps
to prevent unnecessary handleBadgeClick recreations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add missing byWorkflows invalidation to useUpdateTemplate

The onSettled handler was missing the byWorkflows() invalidation
that was dropped during the onSuccess→onSettled migration. Without
this, the deploy modal (useTemplateByWorkflow) would show stale data
after a template update.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add TanStack Query best practices to CLAUDE.md and cursor rules

Add comprehensive React Query best practices covering:
- Hierarchical query key factories with intermediate plural keys
- AbortSignal forwarding in all queryFn implementations
- Targeted cache invalidation over broad .all invalidation
- onSettled for optimistic mutation cache reconciliation
- keepPreviousData only on variable-key queries
- No manual fetch in components rule
- Stable mutation references in useCallback deps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review feedback

- Fix syncedRef regression in use-webhook-management: only set
  syncedRef.current=true when webhook is found, so re-sync works
  after webhook creation (e.g., post-deploy)
- Remove redundant detail(id) invalidation from useUpdateTemplate
  onSettled since onSuccess already populates cache via setQueryData

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address second round of PR review feedback

- Reset syncedRef when blockId changes in use-webhook-management so
  component reuse with a different block syncs the new webhook
- Add response.ok check in postAttribution so non-2xx responses
  throw and trigger TanStack Query retry logic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use lists() prefix invalidation in useCreateWorkspaceCredential

Use workspaceCredentialKeys.lists() instead of .list(workspaceId) so
filtered list queries are also invalidated on credential creation,
matching the pattern used by update and delete mutations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address third round of PR review feedback

- Add nullish coalescing fallback for bonusAmount in referral-code
  to prevent rendering "undefined" when server omits the field
- Reset syncedRef when queryEnabled becomes false so webhook data
  re-syncs when the query is re-enabled without component remount

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address fourth round of PR review feedback

- Add AbortSignal to testMcpServerConnection for consistency
- Wrap handleTestConnection in try/catch for mutateAsync error handling
- Replace broad subscriptionKeys.all with targeted users()/usage() invalidation
- Add intermediate users() key to subscription key factory for prefix matching
- Add comment documenting syncedRef null-webhook behavior
- Fix api-keys.ts silent error swallowing on non-ok responses
- Move deployments.ts cache invalidation from onSuccess to onSettled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: achieve full TanStack Query best practices compliance

- Add intermediate plural keys to api-keys, deployments, and schedules
  key factories for prefix-based invalidation support
- Change copilot-keys from refetchQueries to invalidateQueries
- Add signal parameter to organization.ts fetch functions (better-auth
  client does not support AbortSignal, documented accordingly)
- Move useCreateMcpServer invalidation from onSuccess to onSettled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* ran lint

* Fix tables row count

* Update mothership to match copilot in logs

* improvement(resource): layout

* fix(knowledge): compute KB tokenCount from documents instead of stale column (#3463)

The knowledge_base.token_count column was initialized to 0 and never
updated. Replace with COALESCE(SUM(document.token_count), 0) in all
read queries, which already JOIN on documents with GROUP BY.

* improvement(resources): layout and items

* feat(knowledge): add v1 knowledge base API, Obsidian/Evernote connectors, and docs (#3465)

* feat(knowledge): add v1 knowledge base API, Obsidian/Evernote connectors, and docs

- Add v1 REST API for knowledge bases (CRUD, document management, vector search)
- Add Obsidian and Evernote knowledge base connectors
- Add file type validation to v1 file and document upload endpoints
- Update OpenAPI spec with knowledge base endpoints and schemas
- Add connectors documentation page
- Apply query hook formatting improvements

* fix(knowledge): address PR review feedback

- Remove validateFileType from v1/files route (general file upload, not document-only)
- Reject tag filters when searching multiple KBs (tag defs are KB-specific)
- Cache tag definitions to avoid duplicate getDocumentTagDefinitions call
- Fix Obsidian connector silent empty results when syncContext is undefined

* improvement(connectors): add syncContext to getDocument, clean up caching

- Update docs to say 20+ connectors
- Add syncContext param to ConnectorConfig.getDocument interface
- Use syncContext in Evernote getDocument to cache tag/notebook maps
- Replace index-based cache check with Map keyed by KB ID in search route

* fix(knowledge): address second round of PR review feedback

- Fix Zod .default('text') overriding tag definition's actual fieldType
- Fix encodeURIComponent breaking multi-level folder paths in Obsidian
- Use 413 instead of 400 for file-too-large in document upload
- Add knowledge-bases to API reference docs navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(knowledge): prevent cross-workspace KB access in search

Filter accessible KBs by matching workspaceId from the request,
preventing users from querying KBs in other workspaces they have
access to but didn't specify.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(knowledge): audit resourceId, SSRF protection, recursion depth limit

- Fix recordAudit using knowledgeBaseId instead of newDocument.id
- Add SSRF validation to Obsidian connector (reject private/loopback URLs)
- Add max recursion depth (20) to listVaultFiles

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(obsidian): remove SSRF check that blocks localhost usage

The Obsidian connector is designed to connect to the Local REST API
plugin running on localhost (127.0.0.1:27124). The SSRF check was
incorrectly blocking this primary use case.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(resources): segmented API

* fix(execution): ensure background tasks await post-execution DB status updates (#3466)

The fire-and-forget IIFE in execution-core.ts for post-execution logging could be abandoned when trigger.dev tasks exit, leaving executions permanently stuck in "running" status. Store the promise on LoggingSession so background tasks can optionally await it before returning.

* improvement(resource): sorting and icons

* fix(resource): sorting

* improvement(settings): fix mcp modal, add option to edit JSON and add Sim as an MCP client (#3467)

* improvement(settings): fix mcp modal, add option to edit JSON and add Sim as an MCP client

* added docs link in sidebar

* ack comments

* ack comments

* fixed error msg

* feat(mothership): billing (#3464)

* Billing update

* more billing improvements

* credits UI

* credit purchase safety

* progress

* ui improvements

* fix cancel sub

* fix types

* fix daily refresh for teams

* make max features differentiated

* address bugbot comments

* address greptile comments

* revert isHosted

* address more comments

* fix org refresh bar

* fix ui rounding

* fix minor rounding

* fix upgrade issue for legacy plans

* fix formatPlanName

* fix email dispay names

* fix legacy team reference bugs

* referral bonus in credits

* fix org upgrade bug

* improve logs

* respect toggle for paid users

* fix landing page pro features and usage limit checks

* fixed query and usage

* add unit test

* address more comments

* enterprise guard

* fix limits bug

* pass period start/end for overage

* fix(sidebar): restore drag-and-drop for workflows and folders (#3470)

* fix(sidebar): restore drag-and-drop for workflows and folders

Made-with: Cursor

* update docs, unrelated

* improvement(tables): consolidation

* feat(schedules): add schedule creator modal for standalone jobs

Add modal to create standalone scheduled jobs from the Schedules page.
Includes POST API endpoint, useCreateSchedule mutation hook, and full
modal with schedule type selection, timezone, lifecycle, and live preview.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(schedules): add edit support with context menu for standalone jobs

* style(schedules): apply linter formatting

* improvement: tables, favicon

* feat(files): inline file viewer with text editing (#3475)

* feat(files): add inline file viewer with text editing and create file modal

Add file preview/edit functionality to the workspace files page. Text files
(md, json, txt, yaml, etc.) open in an editable textarea with Cmd/Ctrl+S save.
PDFs render in an iframe. New file button creates empty .md files via a modal.
Uses ResourceHeader breadcrumbs and ResourceOptionsBar for save/download/delete.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(files): add UX polish, PR review fixes, and context menu

- Add unsaved changes guard modal (matching credentials manager pattern)
- Add delete confirmation modal for both viewer and context menu
- Add save status feedback (Save → Saving... → Saved)
- Add right-click context menu with Open, Download, Delete actions
- Add 50MB file size limit on content update API
- Add storage quota check before content updates
- Add response.ok guard on download to prevent corrupt files
- Add skeleton loading for pending file selection (prevents flicker)
- Fix updateContent in handleSave dependency array

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): propagate save errors and remove redundant sizeDiff

- Remove try/catch in TextEditor.handleSave so errors propagate to
  parent, which correctly shows save failure status
- Remove redundant inner sizeDiff declaration that shadowed outer scope

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): remove unused textareaRef

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): move Cmd+S to parent, add save error feedback, hide save for non-text files

- Move Cmd+S keyboard handler from TextEditor to Files so it goes
  through the parent handleSave with proper status management
- Add 'error' save status with red "Save failed" label that auto-resets
- Only show Save button for text-editable file types (md, txt, json, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(files): add save tooltip, deduplicate text-editable extensions

- Add Tooltip on Save button showing Cmd+S / Ctrl+S shortcut
- Export TEXT_EDITABLE_EXTENSIONS from file-viewer and reuse in files.tsx
  instead of duplicating the list inline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: extract isMacPlatform to shared utility

Move isMacPlatform() from global-commands-provider.tsx to
lib/core/utils/platform.ts so it can be reused by files.tsx tooltip
without duplication.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(files): deduplicate delete modal, use shared formatFileSize

- Extract DeleteConfirmModal component to eliminate duplicate modal
  markup between viewer and list modes
- Replace local formatFileSize with shared utility from file-utils.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): fix a11y label lint error and remove mutation object from useCallback deps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): add isDirty guard on handleSave, return proper HTTP status codes

Prevents "Saving → Saved" flash when pressing Cmd+S with no changes.
Returns 404 for file-not-found and 402 for quota-exceeded instead of 500.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): reset isDirty/saveStatus on delete and discard, remove deprecated navigator.platform

- Clear isDirty and saveStatus when deleting the currently-viewed file to
  prevent spurious beforeunload prompts
- Reset saveStatus on discard to prevent stale "Save failed" when opening
  another file
- Remove deprecated navigator.platform, userAgent fallback covers all cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): prevent concurrent saves on rapid Cmd+S, add YAML MIME types

- Add saveStatus === 'saving' guard to handleSave to prevent duplicate
  concurrent PUT requests from rapid keyboard shortcuts
- Add yaml/yml MIME type mappings to getMimeTypeFromExtension

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(files): reuse shared extension constants, parallelize cancelQueries

- Replace hand-rolled SUPPORTED_EXTENSIONS with composition from existing
  SUPPORTED_DOCUMENT/AUDIO/VIDEO_EXTENSIONS in validation.ts
- Parallelize sequential cancelQueries calls in delete mutation onMutate

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): guard handleCreate against duplicate calls while pending

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): show upload progress on the Upload button, not New file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): use ref-based guard for create pending state to avoid stale closure

The uploadFile.isPending check was stale because the mutation object
is excluded from useCallback deps (per codebase convention). Using a
ref ensures the guard works correctly across rapid Enter key presses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* cleanup(files): use shared icon import, remove no-op props, wrap handler in useCallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* improvement: tables, dropdown

* improvement(docs): align sidebar method badges and polish API reference styling (#3484)

* improvement(docs): align sidebar method badges and polish API reference styling

* fix(docs): revert className prop on DocsPage for CI compatibility

* fix(docs): restore oneOf schema for delete rows and use rem units in CSS

* fix(docs): replace :has() selectors with direct className for reliable prod layout

The API docs layout was intermittently narrow in production because CSS
:has(.api-page-header) selectors are unreliable in Tailwind v4 production
builds. Apply className="openapi-page" directly to DocsPage and replace
all 64 :has() selectors with .openapi-page class targeting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(docs): bypass TypeScript check for className prop on DocsPage

Use spread with type assertion to pass className to DocsPage, working
around a CI type resolution issue where the prop exists at runtime but
is not recognized by TypeScript in the Vercel build environment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(docs): use inline style tag for grid layout, revert CSS to :has() selectors

The className prop on DocsPage doesn't exist in the fumadocs-ui version
resolved on Vercel, so .openapi-page was never applied and all 64 CSS
rules broke. Revert to :has(.api-page-header) selectors for styling and
use an inline <style> tag for the critical grid-column layout override,
which is SSR'd and doesn't depend on any CSS selector matching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(docs): add pill styling to footer navigation method badges

The footer nav badges (POST, GET, etc.) had color from data-method rules
but lacked the structural pill styling (padding, border-radius, font-size).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(docs): use named grid lines instead of numeric column indices (#3487)

Root cause: the fumadocs grid template has 3 columns in production but
5 columns in local dev. Our CSS used `grid-column: 3 / span 2` which
targeted the wrong column in the 3-column grid, placing content in
the near-zero-width TOC column instead of the main content column.

Fix: use `grid-column: main-start / toc-end` which uses CSS named grid
lines from grid-template-areas, working regardless of column count.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(resource): layout

* improvement: icon, resource header options

* improvement: icons

* fix(files): icon

* feat(tables): column operations, row ordering, V1 API (#3488)

* feat(tables): add column operations, row ordering, V1 columns API, and OpenAPI spec

Adds column rename/delete/type change/constraint updates to the tables module,
row ordering via position column, UI metadata schema, V1 public API for column
operations with rate limiting and audit logging, and OpenAPI documentation.

Key changes:
- Service-layer column operations with validation (name pattern, type compatibility, unique/required constraints)
- Position column on user_table_rows with composite index for efficient ordering
- V1 /api/v1/tables/{tableId}/columns endpoint (POST/PATCH/DELETE) with rate limiting and audit
- Shared Zod schemas extracted to table/utils.ts using COLUMN_TYPES constant
- Targeted React Query invalidation (row vs schema mutations) with consistent onSettled usage
- OpenAPI 3.1.0 spec for columns endpoint with code samples
- Position field added to all row response mappings for consistency
- Sort fallback to position ordering when buildSortClause returns null

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): use specific error prefixes instead of broad "Cannot" match

Prevents internal TypeErrors (e.g. "Cannot read properties of undefined")
from leaking as 400 responses. Now matches only domain-specific errors:
"Cannot delete the last column" and "Cannot set column".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): reject Infinity and NaN in number type compatibility check

Number.isFinite rejects Infinity, -Infinity, and NaN, preventing
non-finite values from passing column type validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): invalidate table list on row create/delete for stale rowCount

Row create and delete mutations now invalidate the table list cache since
it includes a computed rowCount. Row updates (which don't change count)
continue to only invalidate row queries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): add column name length check, deduplicate name gen, reset pagination on clear

- Add MAX_COLUMN_NAME_LENGTH validation to addTableColumn (was missing,
  renameColumn already had it)
- Extract generateColumnName helper to eliminate triplicated logic across
  handleAddColumn, handleInsertColumnLeft, handleInsertColumnRight
- Reset pagination to page 0 when clearing sort/filter to prevent showing
  empty pages after narrowing filters are removed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: hoist tableId above try block in V1 columns route, add detail invalidation to invalidateRowCount

- V1 columns route: `tableId` was declared inside `try` but referenced in
  `catch` logger.error, causing undefined in error logs. Hoisted `await params`
  above try in all three handlers (POST, PATCH, DELETE).
- invalidateRowCount: added `tableKeys.detail(tableId)` invalidation since the
  single-table GET response includes `rowCount`, which becomes stale after
  row create/delete without this.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add position to all row mutation responses, remove dead filter code

- Add `position` field to POST (single + batch) and PATCH row responses
  across both internal and V1 routes, matching GET responses and OpenAPI spec.
- Remove unused `filterConfig`, `handleFilterToggle`, `handleFilterClear`,
  and `activeFilters` — dead code left over from merge conflict resolution.
  `handleFilterApply` (the one actually wired to JSX) is preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: invalidateTableSchema now also invalidates table list cache

Column add/rename/delete/update mutations now invalidate tableKeys.list()
since the list endpoint returns schema.columns for each table. Without this,
the sidebar table list would show stale column schemas until staleTime expires.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: replace window.prompt/confirm with emcn Modal dialogs

Replace non-standard browser dialogs with proper emcn Modal components
to match the existing codebase pattern (e.g. delete table confirmation).

- Column rename: Modal with Input field + Enter key support
- Column delete: Modal with destructive confirmation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* update schedule creation ui and run lint

* improvement: logs

* improvement(tables): multi-select and efficiencies

* Table tools

* improvement(folder-selection): folder deselection + selection order should match visual

* fix(selections): more nested folder inaccuracies

* Tool updates

* Store tool call results

* fix(landing): wire agent input to mothership

* feat(mothership): resource viewer

* fix tests

* fix(streaming): smoother streaming with throttled rendering, ResizeObserver scroll, and batched updates (#3471)

* fix(streaming): smoother streaming with throttled rendering, ResizeObserver scroll, and batched updates

- Add useThrottledValue hook (100ms trailing-edge throttle) to gate DOM re-renders during streaming across all chat surfaces
- Replace 100ms setInterval scroll polling with ResizeObserver-based auto-scroll, programmatic scroll timestamp tracking, and nested [data-scrollable] region handling
- Extract processContentBuffer from inline content handler for cleaner code organization in copilot SSE handlers
- Add RAF-based update batching (50ms max interval) to floating chat and home chat streaming paths
- Add useProgressiveList hook for progressive rendering of long conversation histories via requestAnimationFrame

Made-with: Cursor

* ack PR comments

* fix search modal

* more comments

* ack comments

* count

* ack comments

* ack comment

* improvement(mothership): worklfow resource

* Fix tool call persistence in chat

* Tool results

* Fix error status

* File uploads to mothership

* feat(templates): landing page templates workflow states

* improvement(mothership): chat stability

* improvement(mothership): chat history and stability

* improvement(tables): click-to-select navigation, inline rename, column resize (#3496)

* improvement(tables): click-to-select navigation, inline rename, column resize

* fix(tables): address PR review comments

- Add doneRef guard to useInlineRename preventing Enter+blur double-fire
- Fix PATCH error handler: return 500 for non-validation errors, fix unreachable logger.error
- Stop click propagation on breadcrumb rename input

* fix(tables): add rows-affected check in renameTable service

Prevents silent no-op when tableId doesn't match any record.

* fix(tables): useMemo deps + placeholder memo initialCharacter check

- Use primitive editingId/editValue in useMemo deps instead of whole
  useInlineRename object (which creates a new ref every render)
- Add initialCharacter comparison to placeholderPropsAreEqual, matching
  the existing pattern in dataRowPropsAreEqual

* fix(tables): address round 2 review comments

- Mirror name validation (regex + max length) in PatchTableSchema so
  validateTableName failures return 400 instead of 500
- Add .returning() + rows-affected check to renameWorkspaceFile,
  matching the renameTable pattern
- Check response.ok before parsing JSON in useRenameWorkspaceFile,
  matching the useRenameTable pattern

* refactor(tables): reuse InlineRenameInput in BreadcrumbSegment

Replace duplicated inline input markup with the shared component.
Eliminates redundant useRef, useEffect, and input boilerplate.

* fix(tables): set doneRef in cancelRename to prevent blur-triggered save

Escape → cancelRename → input unmounts → blur → submitRename would
save instead of canceling. Now cancelRename sets doneRef like
submitRename does, blocking the subsequent blur handler.

* fix(tables): pointercancel cleanup + typed FileConflictError

- Add pointercancel handler to column resize to prevent listener leaks
  when system interrupts the pointer (touch-action override, etc.)
- Replace stringly-typed error.message.includes('already exists') with
  FileConflictError class for refactor-safe 409 status detection

* fix(tables): stable useCallback dep + rename shadowed variable

- Use listRename.startRename (stable ref) instead of whole listRename
  object in handleContextMenuRename deps
- Rename inner 'target' to 'origin' in arrow-key handler to avoid
  shadowing the outer HTMLElement 'target'

* fix(tables): move class below imports, stable submitRename, clear editingCell

- Move FileConflictError below import statements (import-first convention)
- Make submitRename a stable useCallback([]) by reading editingId and
  editValue through refs (matches existing onSaveRef pattern)
- Add setEditingCell(null) to handleEmptyRowClick for symmetry with
  handleCellClick

* feat(tables): persist column widths in table metadata

Column widths now survive navigation and page reloads. On resize-end,
widths are debounced (500ms) and saved to the table's metadata field
via a new PUT /api/table/[tableId]/metadata endpoint. On load, widths
are seeded from the server once via React Query.

* fix type checking for file viewer

* fix(tables): address review feedback — 4 fixes

1. headerRename.onSave now uses the fileId parameter directly instead
   of the selectedFile closure, preventing rename-wrong-file race
2. updateMetadataMutation uses ref pattern matching mutateRef/createRef
3. Type-to-enter filters non-numeric chars for number columns, non-date
   chars for date columns
4. renameValue only passed to actively-renaming ColumnHeaderMenu,
   preserving React.memo for other columns

* fix(tables): position-based gap rows, insert above/below, consistency fixes

- Fix gap row insert shifting: only shift rows when target position is
  occupied, preventing unnecessary displacement of rows below
- Switch to position-based indexing throughout (positionMap, maxPosition)
  instead of array-index for correct sparse position handling
- Add insert row above/below to context menu
- Use CellContent for pending values in PositionGapRows (matching PlaceholderRows)
- Add belowHeader selection overlay logic to PositionGapRows
- Remove unnecessary 500ms debounce on column width persistence

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix cells nav w keyboard

* added preview panel for html, markdown rendering, completed table

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): one small tables ting (#3497)

* feat(exa-hosted-key): Restore exa hosted key (#3499)

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement(ui): consistent styling

* styling alignment

* improvements(tables): styling improvements

* improve resizer for file preview for html files

* updated document icon

* fix(credentials): exclude regular login methods from credential sync

* update docs

* upgrade turbo

* improvement: tables, chat

* Fix table column delete

* small table rename bug, files updates not persisting

* Table batch ops

* fix(credentials): block usage at execution layer without perms + fix invites

* feat(hosted-key-services) Add hosted key for multiple services (#3461)

* feat(hosted keys): Implement serper hosted key

* Handle required fields correctly for hosted keys

* Add rate limiting (3 tries, exponential backoff)

* Add custom pricing, switch to exa as first hosted key

* Add telemetry

* Consolidate byok type definitions

* Add warning comment if default calculation is used

* Record usage to user stats table

* Fix unit tests, use cost property

* Include more metadata in cost output

* Fix disabled tests

* Fix spacing

* Fix lint

* Move knowledge cost restructuring away from generic block handler

* Migrate knowledge unit tests

* Lint

* Fix broken tests

* Add user based hosted key throttling

* Refactor hosted key handling. Add optimistic handling of throttling for custom throttle rules.

* Remove research as hosted key. Recommend BYOK if throtttling occurs

* Make adding api keys adjustable via env vars

* Remove vestigial fields from research

* Make billing actor id required for throttling

* Switch to round robin for api key distribution

* Add helper method for adding hosted key cost

* Strip leading double underscores to avoid breaking change

* Lint fix

* Remove falsy check in favor for explicit null check

* Add more detailed metrics for different throttling types

* Fix _costDollars field

* Handle hosted agent tool calls

* Fail loudly if cost field isn't found

* Remove any type

* Fix type error

* Fix lint

* Fix usage log double logging data

* Fix test

* Add browseruse hosted key

* Add firecrawl and serper hosted keys

* feat(hosted key): Add exa hosted key (#3221)

* feat(hosted keys): Implement serper hosted key

* Handle required fields correctly for hosted keys

* Add rate limiting (3 tries, exponential backoff)

* Add custom pricing, switch to exa as first hosted key

* Add telemetry

* Consolidate byok type definitions

* Add warning comment if default calculation is used

* Record usage to user stats table

* Fix unit tests, use cost property

* Include more metadata in cost output

* Fix disabled tests

* Fix spacing

* Fix lint

* Move knowledge cost restructuring away from generic block handler

* Migrate knowledge unit tests

* Lint

* Fix broken tests

* Add user based hosted key throttling

* Refactor hosted key handling. Add optimistic handling of throttling for custom throttle rules.

* Remove research as hosted key. Recommend BYOK if throtttling occurs

* Make adding api keys adjustable via env vars

* Remove vestigial fields from research

* Make billing actor id required for throttling

* Switch to round robin for api key distribution

* Add helper method for adding hosted key cost

* Strip leading double underscores to avoid breaking change

* Lint fix

* Remove falsy check in favor for explicit null check

* Add more detailed metrics for different throttling types

* Fix _costDollars field

* Handle hosted agent tool calls

* Fail loudly if cost field isn't found

* Remove any type

* Fix type error

* Fix lint

* Fix usage log double logging data

* Fix test

---------

Co-authored-by: Theodore Li <teddy@zenobiapay.com>

* Fail fast on cost data not being found

* Add hosted key for google services

* Add hosting configuration and pricing logic for ElevenLabs TTS tools

* Add linkup hosted key

* Add jina hosted key

* Add hugging face hosted key

* Add perplexity hosting

* Add broader metrics for throttling

* Add skill for adding hosted key

* Lint, remove vestigial hosted keys not implemented

* Revert agent changes

* fail fast

* Fix build issue

* Fix build issues

* Fix type error

* Remove byok types that aren't implemented

* Address feedback

* Use default model when model id isn't provided

* Fix cost default issues

* Remove firecrawl error suppression

* Restore original behavior for hugging face

* Add mistral hosted key

* Remove hugging face hosted key

* Fix pricing mismatch is mistral and perplexity

* Add hosted keys for parallel and brand fetch

* Add brandfetch hosted key

* Update types

* Change byok name to parallel_ai

* Add telemetry on unknown models

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement(settings): SSR prefetch, code splitting, dedicated skeletons

* fix: bust browser cache for workspace file downloads

The downloadFile function was using a plain fetch() that honored the
aggressive cache headers, causing newly created files to download empty.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(settings): use emcn Skeleton in extracted skeleton files

* fix(settings): extract shared response mappers to prevent server/client shape drift

Addresses PR review feedback — prefetch.ts duplicated response mapping logic from client hooks. Extracted mapGeneralSettingsResponse and mapUserProfileResponse as shared functions used by both client fetch and server prefetch.

* update byok page

* fix(settings): include theme sync in client-side prefetch queryFn

Hover-based prefetchGeneralSettings now calls syncThemeToNextThemes, matching the useGeneralSettings hook behavior so theme updates aren't missed when prefetch refreshes stale cache.

* fix(byok): use EMCN Input for search field instead of ui Input

Replace @/components/ui Input with the already-imported EmcnInput for design-system consistency.

* fix(byok): use ui Input for search bar to match other settings pages

* fix(settings): use emcn Input for file input in general settings

* improvement(settings): add search bar to skeleton loading states

Skeletons now include the search bar (and action button where applicable) so the layout matches the final component 1:1. Eliminates layout shift when the dynamic chunk loads — search bar area is already reserved by the skeleton.

* fix(settings): align skeleton layouts with actual component structures

- Fix list item gap from 12px to 8px across all skeletons (API keys, custom tools, credentials, MCP)
- Add OAuth icon placeholder to credential skeleton
- Fix credential button group gap from 8px to 4px
- Remove incorrect gap-[4px] from credential-sets text column
- Rebuild debug skeleton to match real layout (description + input/button row)
- Add scrollable wrapper to BYOK skeleton with more representative item count

* chore: lint fixes

* improvement(sidebar): match workspace switcher popover width to sidebar

Use Radix UI's built-in --radix-popover-trigger-width CSS variable
instead of hardcoded 160px so the popover matches the trigger width
and responds to sidebar resizing.

* revert hardcoded ff

* fix: copilot, improvement: tables, mothership

* feat: inline chunk editor and table batch ops with undo/redo (#3504)

* feat: inline chunk editor and table batch operations with undo/redo

Replace modal-based chunk editing/creation with inline editor following
the files tab pattern (state-based view toggle with ResourceHeader).
Add batch update API endpoint, undo/redo support, and Popover-based
context menus for tables.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove icons from table context menu PopoverItems

Icons were incorrectly carried over from the DropdownMenu migration.
PopoverItems in this codebase use text-only labels.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: restore DropdownMenu for table context menu

The table-level context menu was incorrectly migrated to Popover during
conflict resolution. Only the row-level context menu uses Popover; the
table context menu should remain DropdownMenu with icons, matching the
base branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: bound cross-page chunk navigation polling to max 50 retries

Prevent indefinite polling if page data never loads during
chunk navigation across page boundaries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: navigate to last page after chunk creation for multi-page documents

After creating a chunk, navigate to the last page (where new chunks
append) before selecting it. This prevents the editor from showing
"Loading chunk..." when the new chunk is not on the current page.
The loading state breadcrumb remains as an escape hatch for edge cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add duplicate rowId validation to BatchUpdateByIdsSchema

Adds a .refine() check to reject duplicate rowIds in batch update
requests, consistent with the positions uniqueness check on batch insert.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review comments

- Fix disableEdit logic: use || instead of && so connector doc chunks
  cannot be edited from context menu (row click still opens viewer)
- Add uniqueness validation for rowIds in BatchUpdateByIdsSchema
- Fix inconsistent bg token: bg-background → bg-[var(--bg)] in Pagination

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove duplicate rowId uniqueness refine on BatchUpdateByIdsSchema

The refine was applied both on the inner updates array and the outer
object. Keep only the inner array refine which is cleaner.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address additional PR review comments

- Fix stale rowId after create-row redo: patch undo stack with new row
  ID using patchUndoRowId so subsequent undo targets the correct row
- Fix text color tokens in Pagination: use CSS variable references
  (text-[var(--text-body)], text-[var(--text-secondary)]) instead of
  Tailwind semantic tokens for consistency with the rest of the file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove dead code and fix type errors in table context menu

Remove unused `onAddData` prop and `isEmptyCell` variable from row context
menu (introduced in PR but never wired to JSX). Fix type errors in
optimistic update spreads by removing unnecessary `as Record<string, unknown>`
casts that lost the RowData type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: prevent false "Saved" status on invalid content and mark fire-and-forget goToPage calls

ChunkEditor.handleSave now throws on empty/oversized content instead of
silently returning, so the parent's catch block correctly sets saveStatus
to 'error'. Also added explicit `void` to unawaited goToPage(1) calls
in filter handlers to signal intentional fire-and-forget.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: handle stale totalPages in handleChunkCreated for new-page edge case

When creating a chunk that spills onto a new page, totalPages in the
closure is stale. Now polls displayChunksRef for the new chunk, and if
not found, checks totalPagesRef for an updated page count and navigates
to the new last page before continuing to poll.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Streaming fix -- need to test more

* Make mothership block use long input instead of prompt input

* improvement(billing): isAnnual metadata + docs updates (#3506)

* improvement(billing): on demand toggling and infinite limits

* store stripe metadata to distinguish annual vs monthly

* udpate docs

* address bugbot

* Add piping

* feat(clean-hosted-keys) Remove eleven labs, browseruse. Tweak firecrawl and mistral key impl (#3503)

* Remove eleven labs, browseruse, and firecrawl

* Remove creditsUsed output

* Add back mistral hosting for mistral blocks

* Add back firecrawl since they queue up concurrent requests

* Fix price calculation, remove agent since its super long running and will clog up queue

* Define hosting per tool

* Remove redundant token finding

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* Update vfs to handle hosted keys

* improvement(tables): fix cell editing flash, batch API docs, and UI polish (#3507)

* fix: show text cursor in chunk editor and ensure textarea fills container

Add cursor-text to the editor wrapper so the whole area shows a text
cursor. Click on empty space focuses the textarea. Changed textarea from
h-full/w-full to flex-1/min-h-0 so it properly fills the flex container.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(tables): fix cell editing flash, add batch API docs, and UI polish

Fix stale-data flash when saving inline cell edits by using TanStack Query's
isPending+variables pattern instead of manual cache writes. Also adds OpenAPI
docs for batch table endpoints, DatePicker support in row modal, duplicate row
in context menu, and styling improvements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove dead resolveColumnFromEvent callback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: unify paste undo into single create-rows action

Batch-created rows from paste now push one `create-rows` undo entry
instead of N individual `create-row` entries, so a single Ctrl+Z
reverses the entire paste operation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: validate dates in inline editor and displayToStorage

InlineDateEditor now validates computed values via Date.parse before
saving, preventing invalid strings like "hello" from being sent to the
server. displayToStorage now rejects out-of-range month/day values
(e.g. 13/32) instead of producing invalid YYYY-MM-DD strings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: accept ISO date format in inline date editor

Fall back to raw draft input when displayToStorage returns null, so
valid ISO dates like "2024-03-15" pasted or typed directly are
accepted instead of silently discarded. Date.parse still validates
the final value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add ISO date support to displayToStorage and fix picker Escape

displayToStorage now recognizes YYYY-MM-DD input directly, so ISO
dates typed or pasted work correctly for both saving and picker sync.

DatePicker Escape now refocuses the input instead of saving, so the
user can press Escape again to cancel or Enter to confirm — matching
the expected cancel behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove dead paste boundary check

The totalR guard in handlePaste could never trigger since totalR
included pasteRows.length, making targetRow always < totalR.
Remove the unused variable and simplify the selection focus calc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* update openapi

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix dysfunctional unique operation in tables

* feat(autosave): files and chunk editor autosave with debounce + refetch  (#3508)

* feat(files): debounced autosave while editing

* address review comments

* more comments

* fix: unique constraint check crash and copilot table initial rows

- Fix TypeError in updateColumnConstraints: db.execute() returns a
  plain array with postgres-js, not { rows: [...] }. The .rows.length
  access always crashed, making "Set unique" completely broken.

- Add initialRowCount: 20 to copilot table creation so tables created
  via chat have the same empty rows as tables created from the UI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix signaling

* revert: remove initialRowCount from copilot table creation

Copilot populates its own data after creating a table, so pre-creating
20 empty rows causes data to start at position 21 with empty rows above.
initialRowCount only makes sense for the manual UI creation flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improvement: chat, workspace header

* chat metadata

* Fix schema mismatch (#3510)

Co-authored-by: Theodore Li <theo@sim.ai>

* Fixes

* fix: manual table creation starts with 1 row, 1 column

Manual tables now create with a single 'name' column and 1 row instead
of 2 columns and 20 rows. Copilot tables remain at 0 rows, 0 columns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: horizontal scroll in embedded table by replacing overflow-hidden with overflow-clip

Cell content spans used Tailwind's `truncate` (overflow: hidden), creating
scroll containers that consumed trackpad wheel events on macOS without
propagating to the actual scroll ancestor. Replaced with overflow-clip
which clips identically but doesn't create a scroll container. Also moved
focus target from outer container to the scroll div for correctness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix tool call ordering

* Fix tests

* feat: add task multi-select, context menu, and subscription UI updates

Add shift-click range selection, cmd/ctrl-click toggle, and right-click
context menu for tasks in sidebar matching workflow/folder patterns.
Update subscription settings tab UI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(credentials): autosync behaviour cross workspace (#3511)

* fix(credentials): autosync behaviour cross workspace

* address comments

* fix(api-key-reminder) Add reminder on hosted keys that api key isnt needed (#3512)

* Add reminder on hosted keys that api key isnt needed

* Fix test case

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement: sidebar, chat

* Usage limit

* Plan prompt

* fix(sidebar): workspace header collapse

* fix(sidebar): task navigation

* Subagent tool call persistence

* Don't drop suabgent text

* improvement(ux): streaming

* improvement: thinking

* fix(random): optimized kb connector sync engine, rerenders in tables, files, editors, chat (#3513)

* optimized kb connector sync engine, rerenders in tables, files, editors, chat

* refactor(sidebar): rename onTaskClick to onMultiSelectClick for clarity

Made-with: Cursor

* ack comments, add docsFailed

* feat(email-footer) Add "sent with sim ai" for free users (#3515)

* Add "sent with sim ai" for free users

* Only add prompt injection on free tier

* Add try catch around billing info fetch

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement: modals

* ran migrations

* fix(mothership): fix hardcoded workflow color, tables drag line overflowing

* feat(mothership): file attachment indicators, persistence, and chat input improvements

- Show image thumbnails and file-icon cards above user messages in mothership chat
- Persist file attachment metadata (key, filename, media_type, size) in DB with user messages
- Restore attachments from history via /api/files/serve/ URLs so they survive refresh/navigation
- Unify all chat file inputs to use shared CHAT_ACCEPT_ATTRIBUTE constant
- Fix file thumbnail overflow: use flex-wrap instead of hidden horizontal scroll
- Compact attachment cards in floating workflow chat messages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improvement: search modal

* improvement(usage): free plan to 1000 credits  (#3516)

* improvement(billing): free plan to five dollars

* fix comment

* remove per month terminology from marketing

* generate migration

* remove migration

* add migration back

* feat(workspace): add workspace color changing, consolidate update hooks, fix popover dismiss

- Add workspace color change via context menu, reusing workflow ColorGrid UI
- Consolidate useUpdateWorkspaceName + useUpdateWorkspaceColor into useUpdateWorkspace
- Fix popover hover submenu dismiss by using DismissableLayerBranch with pointerEvents
- Remove passthrough wrapper for export, reuse Workspace type for capturedWorkspaceRef
- Reorder log columns: workflow first, merge date+time into single column

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update oauth cred tool

* fix(diff-controls): fixed positioning for copilot diff controls

* fix(font): added back old font for emcn code editor

* improvement: panel, special tags

* improvement: chat

* improvement: loading and file dropping

* feat(templates): create home templates

* fix(uploads): resolve .md file upload rejection and deduplicate file type utilities

Browsers report empty or application/octet-stream MIME types for .md files,
causing copilot uploads to be rejected. Added resolveFileType() utility that
falls back to extension-based MIME resolution at both client and server
boundaries. Consolidated duplicate MIME mappings into module-level constants,
removed duplicate isImageFileType from copilot module, and replaced hardcoded
ALLOWED_EXTENSIONS with composition from shared validation constants. Also
switched file attachment previews to use shared getDocumentIcon utility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(home): prevent initial view from being scrollable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* autofill fixes

* added back integrations page, reverted secrets page back to old UI

* Fix workspace dropdown getting cut off when sidebar is collapsed

* fix(mothership): lint (#3517)

* fix(mothership): lint

* fix typing

* fix tests

* fix stale query

* fix plan display name

* Feat/add mothership manual workflow runs (#3520)

* Add run and open workflow buttons in workflow preview

* Send log request message after manual workflow run

* Make edges in embedded workflow non-editable

* Change chat to pass in log as additional context

* Revert "Change chat to pass in log as additional context"

This reverts commit e957dffb2f.

* Revert "Send log request message after manual workflow run"

This reverts commit 0fb92751f0.

* Move run and workflow icons to tab bar

* Simplify boolean condition

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* feat(resource-tab-scroll): Allow vertical scrolling to scroll resource tab

* fix(remove-speed-hosted-key) Remove maps speed limit hosted key, it's deprecated (#3521)

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement: home, sidebar

* fix(download-file): render correct file download link for mothership (#3522)

* fix(download-file): render correct file download link for mothership

* Fix uunecessary call

* Use simple strip instead of db lookup and moving behavior

* Make regex strip more strict

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement: schedules, auto-scroll

* fix(settings): navigate back to origin page instead of always going home

Use sessionStorage to store the return URL when entering settings, and
use router.replace for tab switches so history doesn't accumulate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(schedules): release lastQueuedAt lock on all exit paths to prevent stuck schedules

Multiple error/early-return paths in executeScheduleJob and executeJobInline
were exiting without clearing lastQueuedAt, causing the dueFilter to permanently
skip those schedules — resulting in stale "X hours ago" display for nextRunAt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(mothership): inline rename for resource tabs + workspace_file rename tool

- Add double-click inline rename on file and table resource tabs
- Wire useInlineRename + useRenameWorkspaceFile/useRenameTable mutations
- Add rename operation to workspace_file copilot tool (schema, server, router)
- Add knowledge base resource support (type, extraction, rendering, actions)
- Accept optional className on InlineRenameInput for context-specific sizing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: remove inline rename UI from resource tabs

Keep the workspace_file rename tool for the mothership agent.
Only the UI-side inline rename (double-click tabs) is removed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(mothership): knowledge base resource extraction + Resource/ResourceTable refactor

- Extract KB resources from knowledge subagent respond format (knowledge_bases array)
- Add knowledge_base tool to RESOURCE_TOOL_NAMES and TOOL_UI_METADATA
- Extract ResourceTable as independently composable memoized component
- Move contentOverride/overlay to Resource shell level (not table primitive)
- Remove redundant disableHeaderSort and loadingRows props
- Rename internal sort state for clarity (sort → internalSort, sortOverride → externalSort)
- Export ResourceTable and ResourceTableProps from barrel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(logs) Run workflows client side in mothership to transmit logs (#3529)

* Run workflows client side in mothership to transmit logs

* Initialize set as constant, prevent duplicate execution

* Fix lint

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* fix(import) fix missing file

* fix(resource): Hide resources that have been deleted (#3528)

* Hide resources that have been deleted

* Handle table, workflow not found

* Add animation to prevent flash when previous resource was deleted

* Fix animation playing on every switch

* Run workflows client side in mothership to transmit logs

* Fix race condition for animation

* Use shared workflow tool util file

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* fix: chat scrollbar on sidebar collapse/open

* edit existing workflow should bring up artifact

* fix(agent) subagent and main agent text being merged without spacing

* feat(mothership): remove resource-level delete tools from copilot

Remove delete operations for workflows, folders, tables, and files
from the mothership copilot to prevent destructive actions via AI.
Row-level and column-level deletes are preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: stop sidebar from auto-collapsing when resource panel appears (#3540)

The sidebar was forcibly collapsed whenever a resource (e.g. workflow)
first appeared in the resource panel during a task. This was disruptive
on larger screens where users want to keep both the sidebar and resource
panel visible simultaneously.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(mothership): insert copilot-created workflows at top of list (#3537)

* feat(mothership): remove resource-level delete tools from copilot

Remove delete operations for workflows, folders, tables, and files
from the mothership copilot to prevent destructive actions via AI.
Row-level and column-level deletes are preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(mothership): insert copilot-created workflows at top of list

* fix(mothership): server-side top-insertion sort order and deduplicate registry logic

* fix(mothership): include folder sort orders when computing top-insertion position

* fix(mothership): use getNextWorkflowColor instead of hardcoded color

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(stop) Add stop of motehership ran workflows, persist stop messages (#3538)

* Connect play stop workflow in embedded view to workflow

* Fix stop not actually stoping workflow

* Fix ui not showing stopped by user

* Lint fix

* Plumb cancellation through system

* Stopping mothership chat stops workflow

* Remove extra fluff

* Persist blocks on cancellation

* Add root level stopped by user

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* fix(autolayout): targetted autolayout heuristic restored (#3536)

* fix(autolayout): targetted autolayout heuristic restored

* fix autolayout boundary cases

* more fixes

* address comments

* on conflict updates

* address more comments

* fix relative position scope

* fix tye omission

* address bugbot comment

* Credential tags

* Credential id field

* feat(mothership): server-persisted unread task indicators via SSE (#3549)

* feat(mothership): server-persisted unread task indicators via SSE

Replace fragile client-side polling + timer-based green flash with
server-persisted lastSeenAt semantics, real-time SSE push via Redis
pub/sub, and dot overlay UI on the Blimp icon.

- Add lastSeenAt column to copilotChats for server-persisted read state
- Add Redis/local pub/sub singleton for task status events (started,
  completed, created, deleted, renamed)
- Add SSE endpoint (GET /api/mothership/events) with heartbeat and
  workspace-scoped filtering
- Add mark-read endpoint (POST /api/mothership/chats/read)
- Publish SSE events from chat, rename, delete, and auto-title handlers
- Add useTaskEvents hook for client-side SSE subscription
- Add useMarkTaskRead mutation with optimistic update
- Replace timer logic in sidebar with TaskStatus state machine
  (running/unread/idle) and dot overlay using brand color variables
- Mark tasks read on mount and stream completion in home page
- Fix security: add userId check to delete WHERE clause
- Fix: bump updatedAt on stream completion
- Fix: set lastSeenAt on rename to prevent false-positive unread

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review feedback

- Return 404 when delete finds no matching chat (was silent no-op)
- Move log after ownership check so it only fires on actual deletion
- Publish completed SSE event from stop route so sidebar dot clears on abort

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: backfill last_seen_at in migration to prevent false unread dots

Existing rows would have last_seen_at = NULL after migration, causing
all past completed tasks to show as unread. Backfill sets last_seen_at
to updated_at for all existing rows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: timestamp mismatch on task creation + wasSendingRef leak across navigation

- Pass updatedAt explicitly alongside lastSeenAt on chat creation so
  both use the same JS timestamp (DB defaultNow() ran later, causing
  updatedAt > lastSeenAt → false unread)
- Reset wasSendingRef when chatId changes to prevent a stale true
  from task A triggering a redundant markRead on task B

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: mark-read fires for inline-created chats + encode workspaceId in SSE URL

Expose resolvedChatId from useChat so home.tsx can mark-read even when
chatId prop stays undefined after replaceState URL update. Also
URL-encode workspaceId in EventSource URL as a defensive measure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: auto-focus home input on initial view + fix sidebar task click handling

Auto-focus the textarea when the initial home view renders. Also fix
sidebar task click to always call onMultiSelectClick so selection state
stays consistent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: auto-title sets lastSeenAt + move started event inside DB guard

Auto-title now sets both updatedAt and lastSeenAt (matching the rename
route pattern) to prevent false-positive unread dots. Also move the
'started' SSE event inside the if(updated) guard so it only fires when
the DB update actually matched a row.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* modified tasks multi select to be just like workflows

* fix

* refactor: extract generic pub/sub and SSE factories + fixes

- Extract createPubSubChannel factory (lib/events/pubsub.ts) to eliminate
  duplicated Redis/EventEmitter boilerplate between task and MCP pub/sub
- Extract createWorkspaceSSE factory (lib/events/sse-endpoint.ts) to share
  auth, heartbeat, and cleanup logic across SSE endpoints
- Fix auto-title race suppressing unread status by removing updatedAt/lastSeenAt
  from title-only DB update
- Fix wheel event listener leak in ResourceTabs (RefCallback cleanup was silently
  discarded)
- Fix getFullSelection() missing taskIds (inconsistent with hasAnySelection)
- Deduplicate SSE_RESPONSE_HEADERS to spread from shared SSE_HEADERS
- Hoist isSttAvailable to module-level constant to avoid per-render IIFE

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat(logs): add workflow trigger type for sub-workflow executions (#3554)

* feat(logs): add workflow trigger type for sub-workflow executions

* fix(logs): align workflow filter color with blue-secondary badge variant

* feat(tab) allow user to control resource tabs

* Make resources persist to backend

* Use colored squares for workflows

* Add click and drag functionality to resource

* Fix expanding panel logic

* Reduce duplication, reading resource also opens up resource panel

* Move resource dropdown to own file

* Handle renamed resources

* Clicking already open tab should just switch to tab

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* Fix new resource tab button not appearing on tasks

* improvement(ui): dropdown menus, icons, globals

* improvement: notifications, terminal, globals

* reverted task logic

* feat(context) pass resource tab as context (#3555)

* feat(context) add currenttly open resource file to context for agent

* Simplify resource resolution

* Skip initialize vfs

* Restore ff

* Add back try catch

* Remove redundant code

* Remove json serialization/deserialization loop

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* Feat(references) add at to reference sim resources(#3560)


* feat(chat) add at sign

* Address bugbot issues

* Remove extra chatcontext defs

* Add table and file to schema

* Add icon to chip for files

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement(refactor): move to soft deletion of resources + reliability improvements (#3561)

* improvement(deletion): migrate to soft deletion of resources

* progress

* scoping fixes

* round of fixes

* deduplicated name on workflow import

* fix tests

* add migration

* cleanup dead code

* address bugbot comments

* optimize query

* feat(sim-mailer): email inbox for mothership with chat history and plan gating (#3558)

* feat(sim-mailer): email inbox for mothership with chat history and plan gating

* revert hardcoded ff

* fix(inbox): address PR review comments - plan enforcement, idempotency, webhook auth

- Enforce Max plan at API layer: hasInboxAccess() now checks subscription tier (>= 25k credits or enterprise)
- Add idempotency guard to executeInboxTask() to prevent duplicate emails on Trigger.dev retries
- Add AGENTMAIL_WEBHOOK_SECRET env var for webhook signature verification (Bearer token)

* improvement(inbox): harden security and efficiency from code audit

- Use crypto.timingSafeEqual for webhook secret comparison (prevents timing attacks)
- Atomic claim in executor: WHERE status='received' prevents duplicate processing on retries
- Parallelize hasInboxAccess + getUserEntityPermissions in all API routes (reduces latency)
- Truncate email body at webhook insertion (50k char limit, prevents unbounded DB storage)
- Harden escapeAttr with angle bracket and single quote escaping
- Rename use-inbox.ts to inbox.ts (matches hooks/queries/ naming convention)

* fix(inbox): replace Bearer token auth with proper Svix HMAC-SHA256 webhook verification

- Use per-workspace webhook secret from DB instead of global env var
- Verify AgentMail/Svix signatures: HMAC-SHA256 over svix-id.timestamp.body
- Timing-safe comparison via crypto.timingSafeEqual
- Replay protection via timestamp tolerance (5 min window)
- Join mothershipInboxWebhook in workspace lookup (zero additional DB calls)
- Remove dead AGENTMAIL_WEBHOOK_SECRET env var
- Select only needed workspace columns in webhook handler

* fix(inbox): require webhook secret — reject requests when secret is missing

Previously, if the webhook secret was missing from the DB (corrupted state),
the handler would skip verification entirely and process the request
unauthenticated. Now all three conditions are hard requirements: secret must
exist in DB, Svix headers must be present, and signature must verify.

* fix(inbox): address second round of PR review comments

- Exclude rejected tasks from rate limit count to prevent DoS via spam
- Strip raw HTML from LLM output before marked.parse to prevent XSS in emails
- Track responseSent flag to prevent duplicate emails when DB update fails after send

* fix(inbox): address third round of PR review comments

- Use dynamic isHosted from feature-flags instead of hardcoded true
- Atomic JSON append for chat message persistence (eliminates read-modify-write race)
- Handle cutIndex === 0 in stripQuotedReply (body starts with quote)
- Clean up orphan mothershipInboxWebhook row on enableInbox rollback
- Validate status query parameter against enum in tasks API

* fix(inbox): validate cursor param, preserve code blocks in HTML stripping

- Validate cursor date before using in query (return 400 for invalid)
- Split on fenced code blocks before stripping HTML tags to preserve
  code examples in email responses

* fix(inbox): return 500 on webhook server errors to enable Svix retries

* fix(inbox): remove isHosted guard from hasInboxAccess — feature flag is sufficient

* fix(inbox): prevent double-enable from deleting webhook secret row

* fix(inbox): null-safe stripThinkingTags, encode URL params, surface remove-sender errors

- Guard against null result.content in stripThinkingTags
- Use encodeURIComponent on all AgentMail API path parameters
- Surface handleRemoveSender errors to the user instead of swallowing

* improvement(inbox): remove unused types, narrow SELECT queries, fix optimistic ID collision

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(inbox): add keyboard accessibility to clickable task rows

* fix(inbox): use Svix library for webhook verification, fix responseSent flag, prevent inbox enumeration

- Replace manual HMAC-SHA256 verification with official Svix library per AgentMail docs
- Fix responseSent flag: only set true when email delivery actually succeeds
- Return consistent 401 for unknown inbox and bad signature to prevent enumeration
- Make AgentMailInbox.organization_id optional to match API docs

* chore(db): rebase inbox migration onto feat/mothership-copilot (0172 → 0173)

Sync schema with target branch and regenerate migration as 0173
to avoid conflicts with 0172_silky_magma on feat/mothership-copilot.

* fix(db): rebase inbox migration to 0173 after feat/mothership-copilot divergence

Target branch added 0172_silky_magma, so our inbox migration is now 0173_youthful_stryfe.

* fix(db): regenerate inbox migration after rebase on feat/mothership-copilot

* fix(inbox): case-insensitive email match and sanitize javascript: URIs in email HTML

- Use lower() in isSenderAllowed SQL to match workspace members regardless
  of email case stored by auth provider
- Strip javascript:, vbscript:, and data: URIs from marked HTML output to
  prevent XSS in outbound email responses

* fix(inbox): case-insensitive email match in resolveUserId

Consistent with the isSenderAllowed fix — uses lower() so mixed-case
stored emails match correctly, preventing silent fallback to workspace owner.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Kb args

* refactor(resource): remove logs-specific escape hatches from Resource abstraction

Logs now composes ResourceHeader + ResourceOptionsBar + ResourceTable directly
instead of using Resource with contentOverride/overlay escape hatches. Removes
contentOverride, onLoadMore, hasMore, isLoadingMore from ResourceProps. Adds
ColumnOption to barrel export and fixes table.tsx internal import.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(sim-mailer): download email attachments and pass to LLM as multimodal content

Attachments were only passed as metadata text in the email body. Now downloads
actual file bytes from AgentMail, converts via createFileContent (same path as
interactive chat), and sends as fileAttachments to the orchestrator. Also
parallelizes attachment fetching with workspace context loading, and downloads
multiple attachments concurrently via Promise.allSettled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(connector): add Gmail knowledge base connector with thread-based sync and filtering

Syncs email threads from Gmail into knowledge bases with configurable filters:
label scoping, date range presets, promotions/social exclusion, Gmail search
syntax support, and max thread caps to keep KB size manageable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(connector): add Outlook knowledge base connector with conversation grouping and filtering

Syncs email conversations from Outlook/Office 365 via Microsoft Graph API.
Groups messages by conversationId into single documents. Configurable filters:
folder selection, date range presets, Focused Inbox, KQL search syntax, and
max conversation caps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* cleanup resource definition

* feat(connectors): add 8 knowledge base connectors — Zendesk, Intercom, ServiceNow, Google Sheets, Microsoft Teams, Discord, Google Calendar, Reddit

Each connector syncs documents into knowledge bases with configurable filtering:

- Zendesk: Help Center articles + support tickets with status/locale filters
- Intercom: Articles + conversations with state filtering
- ServiceNow: KB articles + incidents with state/priority/category filters
- Google Sheets: Spreadsheet tabs as LLM-friendly row-by-row documents
- Microsoft Teams: Channel messages (Slack-like pattern) via Graph API
- Discord: Channel messages with bot token auth
- Google Calendar: Events with date range presets and attendee metadata
- Reddit: Subreddit posts with top comments, sort/time filters

All connectors validated against official API docs with bug fixes applied.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(inbox): fetch real attachment binary from presigned URL and persist for chat display

The AgentMail attachment endpoint returns JSON metadata with a download_url,
not raw binary. We were base64-encoding the JSON text and sending it to the
LLM, causing provider rejection. Now we parse the metadata, fetch the actual
file from the presigned URL, upload it to copilot storage, and persist it on
the chat message so images render inline with previews.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* added agentmail domain for mailer

* added docs for sim mailer

* fix(resource) handle resource deletion  deletion (#3568)

* Add handle dragging tab to input chat

* Add back delete tools

* Handle deletions properly with resources view

* Fix lint

* Add permisssions checking

* Skip resource_added event when resource is deleted

* Pass workflow id as context

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* update docs styling, add delete confirmation on inbox

* Fix fast edit route

* updated docs styling, added FAQs, updated content

* upgrade turbo

* fix(knowledge) use consistent empty state for documents page

Replace the centered "No documents yet" text with the standard Resource
table empty state (column headers + create row), matching all other
resource pages. Move "Upload documents" from header action to table
create row as "New documents".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(notifications): polish modal styling, credential display, and trigger filters (#3571)

* fix(notifications): polish modal styling, credential display, and trigger filters

- Show credential display name instead of raw account ID in Slack account selector
- Fix label styling to use default Label component (text-primary) for consistency
- Fix modal body spacing with proper top padding after tab bar
- Replace list-card skeleton with form-field skeleton matching actual layout
- Replace custom "Select a Slack account first" box with disabled Combobox (dependsOn pattern)
- Use proper Label component in WorkflowSelector with consistent gap spacing
- Add overflow badge pattern (slice + +N) to level and trigger filter badges
- Use dynamic trigger options from getTriggerOptions() instead of hardcoded CORE_TRIGGER_TYPES
- Relax API validation to accept integration trigger types (z.string instead of z.enum)
- Deduplicate account rows from credential leftJoin in accounts API
- Extract getTriggerOptions() to module-level constants to avoid per-render calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(notifications): address PR review feedback

- Restore accountId in displayName fallback chain (credentialDisplayName || accountId || providerId)
- Add .default([]) to triggerFilter in create schema to preserve backward compatibility
- Treat empty triggerFilter as "match all" in notification matching logic
- Remove unreachable overflow badge for levelFilter (only 2 possible values)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(settings): add spacing to Sim Keys toggle and replace Sim Mailer icon with Send

Add 24px top margin to the "Allow personal Sim keys" toggle so it doesn't
sit right below the empty state. Replace the Mail envelope icon for Sim
Mailer with a new Send (paper plane) icon matching the emcn icon style.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* standardize back buttons in settings

* feat(restore) Add restore endpoints and ui (#3570)

* Add restore endpoints and ui

* Derive toast from notification

* Auth user if workspaceid not found

* Fix recently deleted ui

* Add restore error toast

* Fix deleted at timestamp mismatch

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* fix type errors

* Lint

* improvements: ui/ux around mothership

* reactquery best practices, UI alignment in restore

* clamp logs panel

* subagent thinking text

* fix build, speedup tests by up to 40%

* Fix fast edit

* Add download file shortcut on mothership file view

* fix: SVG file support in mothership chat and file serving

- Send SVGs as document/text-xml to Claude instead of unsupported
  image/svg+xml, so the mothership can actually read SVG content
- Serve SVGs inline with proper content type and CSP sandbox so
  chat previews render correctly
- Add SVG preview support in file viewer (sandboxed iframe)
- Derive IMAGE_MIME_TYPES from MIME_TYPE_MAPPING to reduce duplication
- Add missing webp to contentTypeMap, SAFE_INLINE_TYPES, binaryExtensions
- Consolidate PREVIEWABLE_EXTENSIONS into preview-panel exports

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: replace image/* wildcard with explicit supported types in file picker

The image/* accept attribute allowed users to select BMP, TIFF, HEIC,
and other image types that are rejected server-side. Replace with the
exact set of supported image MIME types and extensions to match the
copilot upload validation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Context tags

* Fix lint

* improvement: chat and terminal

---------

Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Theodore Li <teddy@zenobiapay.com>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Theodore Li <theodoreqili@gmail.com>
Co-authored-by: Theodore Li <theo@sim.ai>
2026-03-13 21:02:08 -07:00
Vikhyath Mondreti
7e740e617b improvement(copilot): state persistence, subflow recreation, dynamic handle topologies (#3569)
* improvement(copilot): state persistence, subflow recreation, dynamic handle topologies

* address comments
2026-03-13 17:47:02 -07:00
PlaneInABottle
92290029f0 fix(execution): queued execution finalization and async correlation (#3535)
* fix(execution): finalize runs before wrapper recovery

* fix(async): preserve execution correlation across queued runs

* fix(webhooks): pass correlation into preprocessing

* style(webhooks): normalize webhook executor formatting

* fix(async): avoid pre-starting queued execution logs

Let executeWorkflowCore own normal-path logging start so queued workflow and schedule executions persist the richer deployment and environment metadata instead of an earlier placeholder start record.

* fix(async): harden execution finalization guards

Prevent leaked core finalization markers from accumulating while keeping outer recovery paths idempotent. Preserve best-effort logging completion by reusing settled completion promises instead of reopening duplicate terminal writes.

* fix(async): preserve outcomes during cleanup

Keep execution finalization cleanup best-effort so cancellation cleanup failures do not overwrite successful or failed outcomes. Restore webhook processor formatting to the repository Biome style to avoid noisy formatter churn.

* fix(async): keep execution finalization state consistent

Retry minimal logging for early failures, only mark core finalization after a log row actually completes, and let paused completions fall back cleanly.

* fix(async): clean stale finalization guards

Scan all finalized execution ids during TTL cleanup so refreshed keys cannot keep expired guards alive, and cover the reused-id ordering regression.

* fix(async): retry failed error finalization

Allow error finalization to retry after a non-error completion and fallback both fail, and always persist failed/error semantics for completeWithError.

* fix(webhooks): reuse preprocessing execution ids

Thread preprocessing execution identity into queued webhook execution so both phases share the same correlation and logs.

---------

Co-authored-by: test <test@example.com>
2026-03-13 02:55:58 -07:00
PlaneInABottle
d84cba6d19 chore(self-hosting): add health check endpoint (#3562)
Add a simple API health route for deployment platforms and container probes, with focused route coverage.

Co-authored-by: test <test@example.com>
2026-03-13 02:10:39 -07:00
Vikhyath Mondreti
d90f828e88 fix(grain): update to stable version of API (#3556)
* fix(grain): update to stable version of API

* fix prewebhook lookup

* update pending webhook verification infra

* add generic webhook test event verification subblock
2026-03-12 22:48:01 -07:00
Waleed
a8bbab2d21 feat(google-ads): add google ads integration for campaign and ad performance queries (#3360)
* feat(google-ads): add google ads integration for campaign and ad performance queries

* fix(google-ads): add input validation for GAQL query parameters

* fix(google-ads): remove deprecated pageSize param, fix searchSettings nesting, add missing date ranges

* fix(google-ads): validate managerCustomerId before use in login-customer-id header

* chore(docs): regenerate docs after google ads integration

* fix(google-ads): use centralized scope utilities and add type re-export

- Replace hardcoded scopes in auth.ts with getCanonicalScopesForProvider('google-ads')
- Replace hardcoded requiredScopes in block with getScopesForService('google-ads')
- Add type re-export from index.ts barrel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(google-ads): add userinfo scopes to oauth provider config

Align google-ads with all other Google services by including
userinfo.email and userinfo.profile scopes in the centralized
OAUTH_PROVIDERS config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* lint

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 21:08:58 -07:00
Waleed
72bb7e6945 fix(executor): skip Response block formatting for internal JWT callers (#3551)
* fix(executor): skip Response block formatting for internal JWT callers

The workflow executor tool received `{error: true}` despite successful child
workflow execution when the child had a Response block. This happened because
`createHttpResponseFromBlock()` hijacked the response with raw user-defined
data, and the executor's `transformResponse` expected the standard
`{success, executionId, output, metadata}` wrapper.

Fix: skip Response block formatting when `authType === INTERNAL_JWT` since
Response blocks are designed for external API consumers, not internal
workflow-to-workflow calls. Also extract `AuthType` constants from magic
strings across all auth type comparisons in the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(executor): add route-level tests for Response block auth gating

Verify that internal JWT callers receive standard format while external
callers (API key, session) get Response block formatting. Tests the
server-side condition directly using workflowHasResponseBlock and
createHttpResponseFromBlock with AuthType constants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(testing): add AuthType to all hybrid auth test mocks

Route code now imports AuthType from @/lib/auth/hybrid, so test mocks
must export it too. Added AuthTypeMock to @sim/testing and included it
in all 15 test files that mock the hybrid auth module.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:56:02 -07:00
Waleed
4cb0f4a2b0 feat(ashby): add webhook triggers with automatic lifecycle management (#3548)
* feat(ashby): add webhook triggers with automatic lifecycle management

* fix(ashby): address PR review comments

- Restore mode: 'advanced' on updateName sub-block
- Move action after spread in formatWebhookInput to prevent override
- Remove generic webhook trigger (Ashby requires webhookType)

* fix(ashby): throw on unknown triggerId, always include webhookType

* fix(ashby): address PR review feedback - paramVisibility, stageType, json catch

- Add paramVisibility: 'user-only' to apiKey extra field
- Remove stageType from candidateStageChange/candidateHire outputs (TriggerOutput type conflict with 'type' field)
- Add .catch() fallback to .json() parse in createAshbyWebhookSubscription
- Fix candidateStageChange outputs to match actual Ashby application payload structure

* fix(ashby): add missing applicationSubmit outputs, fix delete log branches

- Add candidate, currentInterviewStage, job to applicationSubmit outputs
- Split delete webhook log into ok/404/error branches for accurate logging

* fix(ashby): drain response body on delete, clarify decidedAt description

- Cancel unconsumed response body in ok/404 delete branches to free connections
- Update decidedAt description to note it's typically null at offer creation

* fix(ashby): eliminate double-logging, fix hiringTeam JSDoc

- Remove pre-throw warn/error logs; catch block is single logging point
- Remove hiringTeam from candidateHire JSDoc (TriggerOutput doesn't support arrays)
2026-03-12 15:43:56 -07:00
Waleed
fdd587d6af fix(jira): remove unnecessary projectId dependency from manualIssueKey (#3547)
Issue keys are self-sufficient identifiers in Jira (e.g., PROJ-123).
The manualIssueKey field is a text input where users type the key directly,
so it should not depend on projectId/manualProjectId. This dependency
caused the field to clear unnecessarily when the project selection changed.
2026-03-12 14:08:48 -07:00
Waleed
e7b4da2689 feat(slack): add email field to get user and list users tools (#3509)
* feat(slack): add email field to get user and list users tools

* fix(slack): use empty string fallback for email and make type non-optional

* fix(slack): comment out users:read.email scope pending app review
2026-03-12 13:27:37 -07:00
Waleed
aa0101c666 fix(blocks): clarify condition ID suffix slicing for readability (#3546)
Use explicit hyphen separator instead of relying on slice offset to
implicitly include the hyphen in the suffix, making the intent clearer.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:26:11 -07:00
Waleed
c939f8a76e fix(jira): add explicit fields parameter to search/jql endpoint (#3544)
The GET /rest/api/3/search/jql endpoint requires an explicit `fields`
parameter to return issue data. Without it, only the issue `id` is
returned with all other fields empty. This adds `fields=*all` as the
default when the user doesn't specify custom fields.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 12:51:27 -07:00
Waleed
0b19ad0013 improvement(canvas): enable middle mouse button panning in cursor mode (#3542) 2026-03-12 12:44:15 -07:00
Waleed
3d5141d852 chore(oauth): remove unused github-repo generic OAuth provider (#3543) 2026-03-12 12:39:31 -07:00
Waleed
75832ca007 fix(jira): add missing write:attachment:jira oauth scope (#3541) 2026-03-12 12:13:57 -07:00
Waleed
97f78c60b4 feat(tools): add Fathom AI Notetaker integration (#3531)
* feat(fathom): add Fathom AI Notetaker integration

* fix(fathom): address PR review feedback

- Add response.ok checks to all 5 tool transformResponse functions
- Fix include_summary default to respect explicit false (check undefined)
- Add externalId validation before URL interpolation in webhook deletion

* fix(fathom): address second round PR review feedback

- Remove redundant 204 status check in deleteFathomWebhook (204 is ok)
- Use consistent undefined-guard pattern for all include flags
- Add .catch() fallback on webhook creation JSON parse
- Change recording_id default from 0 to null to avoid misleading sentinel

* fix(fathom): add missing crm_matches to list_meetings transform and fix action_items type

- Add crm_matches pass-through in list_meetings transform (was silently dropped)
- Fix action_items type to match API schema (description, user_generated, completed, etc.)
- Add crm_matches type with contacts, companies, deals, error fields

* fix(fathom): guard against undefined webhook id on creation success

* fix(fathom): add type to nested trigger outputs and fix boolean coercion

- Add type: 'object' to recorded_by and default_summary trigger outputs
- Use val === true || val === 'true' pattern for include flag coercion
  to safely handle both boolean and string values from providerConfig

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
2026-03-12 11:00:07 -07:00
Waleed
9295499405 fix(traces): prevent condition blocks from rendering source agent's timeSegments (#3534)
* fix(traces): prevent condition blocks from rendering source agent's timeSegments

Condition blocks spread their source block's entire output into their own
output. When the source is an agent, this leaked providerTiming/timeSegments
into the condition's output, causing buildTraceSpans to create "Initial
response" as a child of the condition span instead of the agent span.

Two fixes:
- Skip timeSegment child creation for condition block types in buildTraceSpans
- Filter execution metadata (providerTiming, tokens, toolCalls, model, cost)
  from condition handler's filterSourceOutput

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(traces): guard condition blocks from leaked metadata on old persisted logs

Extend isConditionBlockType guards to also skip setting span.providerTiming,
span.cost, span.tokens, and span.model for condition blocks. This ensures
old persisted logs (recorded before the filterSourceOutput fix) don't display
misleading execution metadata on condition spans.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(traces): guard toolCalls fallback path for condition blocks on old logs

The else branch that extracts toolCalls from log.output also needs a
condition block guard, otherwise old persisted logs with leaked toolCalls
from the source agent would render on the condition span.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(traces): extract isCondition to local variable for readability

Cache isConditionBlockType(log.blockType) in a local const at the top
of the forEach loop instead of calling it 6 times per iteration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 01:39:02 -07:00
Waleed
6bcbd15ee6 fix(blocks): remap condition/router IDs when duplicating blocks (#3533)
* fix(blocks): remap condition/router IDs when duplicating blocks

Condition and router blocks embed IDs in the format `{blockId}-{suffix}`
inside their subBlock values and edge sourceHandles. When blocks were
duplicated, these IDs were not updated to reference the new block ID,
causing duplicate handle IDs and broken edge routing.

Fixes all four duplication paths: single block duplicate, copy/paste,
workflow duplication (server-side), and workflow import.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(blocks): deep-clone subBlocks before mutating condition IDs

Shallow copy of subBlocks meant remapConditionIds could mutate the
source data (clipboard on repeated paste, or input workflowState on
import). Deep-clone subBlocks in both regenerateBlockIds and
regenerateWorkflowIds to prevent this.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(blocks): remap condition IDs in regenerateWorkflowStateIds (template use)

The template use code path was missing condition/router ID remapping,
causing broken condition blocks when creating workflows from templates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 01:19:38 -07:00
Vikhyath Mondreti
68d207df94 improvement(webhooks): move non-polling executions off trigger.dev (#3527)
* improvement(webhooks): move non-polling off trigger.dev

* restore constants file

* improve comment

* add unit test to prevent drift
2026-03-11 17:07:24 -07:00
Vikhyath Mondreti
d5502d602b feat(webhooks): dedup and custom ack configuration (#3525)
* feat(webhooks): dedup and custom ack configuration

* address review comments

* reject object typed idempotency key
2026-03-11 15:51:35 -07:00
Waleed
37d524bb0a fix(gmail): RFC 2047 encode subject headers for non-ASCII characters (#3526)
* fix(gmail): RFC 2047 encode subject headers for non-ASCII characters

* Fix RFC 2047 encoded word length limit

Split long email subjects into multiple RFC 2047 encoded words to comply with the 75-character limit per RFC 2047 Section 2. Each encoded word now contains at most 45 bytes of UTF-8 content (producing max 60 chars of base64 + 12 chars overhead = 72 total). Multiple encoded words are separated by CRLF + space (folding whitespace).

Applied via @cursor push command

* fix(gmail): split RFC 2047 encoded words on character boundaries

* fix(gmail): simplify RFC 2047 encoding to match Google's own sample

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-03-11 15:48:07 -07:00
Waleed
19ef526886 fix(webhooks): eliminate redundant DB queries from webhook execution path (#3523)
* fix(webhooks): eliminate redundant DB queries from webhook execution path

* chore(webhooks): remove implementation-detail comments

* fix(webhooks): restore auth-first ordering and add credential resolution warning

- Revert parallel auth+preprocessing to sequential auth→preprocessing
  to prevent rate-limit exhaustion via unauthenticated requests
- Add warning log when credential account resolution fails in background job

* fix(webhooks): restore auth-before-reachability ordering and remove dead credentialAccountUserId field

- Move reachability test back after auth to prevent path enumeration
- Remove dead credentialAccountUserId from WebhookExecutionPayload
- Simplify credential resolution condition in background job
2026-03-11 14:51:04 -07:00
Waleed
ff2a1527ab fix(security): add SSRF protection to database tools and webhook delivery (#3500)
* fix(security): add SSRF protection to database tools and webhook delivery

* fix(security): address review comments on SSRF PR

- Remove Promise.race timeout pattern to avoid unhandled rejections
  (http.request timeout is sufficient for webhook delivery)
- Use safeCompare in verifyCronAuth instead of inline HMAC logic
- Strip IPv6 brackets before validateDatabaseHost in Redis route

* fix(security): allow HTTP webhooks and fix misleading MCP error docs

- Add allowHttp option to validateExternalUrl, validateUrlWithDNS,
  and secureFetchWithValidation to support HTTP webhook URLs
- Pass allowHttp: true for webhook delivery and test endpoints
- Fix misleading JSDoc on createMcpErrorResponse (doesn't log errors)
- Mark unused error param with underscore prefix

* fix(security): forward allowHttp option through redirect validation

Pass allowHttp to validateUrlWithDNS in the redirect handler of
secureFetchWithPinnedIP so HTTP-to-HTTP redirects work when allowHttp
is enabled for webhook delivery.

* fix(security): block localhost when allowHttp is enabled

When allowHttp is true (user-supplied webhook URLs), explicitly block
localhost/loopback in both validateExternalUrl and validateUrlWithDNS
to prevent SSRF against internal services.

* fix(security): always strip multi-line content in sanitizeConnectionError

Take the first line of the error message regardless of length to
prevent leaking sensitive data from multi-line error messages.
2026-03-09 20:28:28 -07:00
Waleed
2e1c639a81 fix(parallel): align integration with Parallel AI API docs (#3501)
* fix(parallel): align integration with Parallel AI API docs

* fix(parallel): keep processor subBlock ID for backwards compatibility

* fix(parallel): move error field to top level per ToolResponse interface

* fix(parallel): guard research_input and prevent domain leakage across operations

* fix(parallel): make url/title nullable in types to match transformResponse

* fix(parallel): revert search_queries param type to string for backwards compatibility
2026-03-09 19:47:30 -07:00
Theodore Li
635179d696 Revert "feat(hosted key): Add exa hosted key (#3221)" (#3495)
This reverts commit 158d5236bc.

Co-authored-by: Theodore Li <teddy@zenobiapay.com>
2026-03-09 10:31:54 -07:00
Waleed
f88926a6a8 fix(webhooks): return empty 200 for Slack to close modals cleanly (#3492)
* fix(webhooks): return empty 200 for Slack to close modals cleanly

* fix(webhooks): add clarifying comment on Slack error path trade-off
2026-03-09 10:11:36 -07:00
Waleed
690b47a0bf chore(monitoring): remove SSE connection tracking and Bun.gc debug instrumentation (#3472) 2026-03-08 17:27:05 -07:00
Theodore Li
158d5236bc feat(hosted key): Add exa hosted key (#3221)
* feat(hosted keys): Implement serper hosted key

* Handle required fields correctly for hosted keys

* Add rate limiting (3 tries, exponential backoff)

* Add custom pricing, switch to exa as first hosted key

* Add telemetry

* Consolidate byok type definitions

* Add warning comment if default calculation is used

* Record usage to user stats table

* Fix unit tests, use cost property

* Include more metadata in cost output

* Fix disabled tests

* Fix spacing

* Fix lint

* Move knowledge cost restructuring away from generic block handler

* Migrate knowledge unit tests

* Lint

* Fix broken tests

* Add user based hosted key throttling

* Refactor hosted key handling. Add optimistic handling of throttling for custom throttle rules.

* Remove research as hosted key. Recommend BYOK if throtttling occurs

* Make adding api keys adjustable via env vars

* Remove vestigial fields from research

* Make billing actor id required for throttling

* Switch to round robin for api key distribution

* Add helper method for adding hosted key cost

* Strip leading double underscores to avoid breaking change

* Lint fix

* Remove falsy check in favor for explicit null check

* Add more detailed metrics for different throttling types

* Fix _costDollars field

* Handle hosted agent tool calls

* Fail loudly if cost field isn't found

* Remove any type

* Fix type error

* Fix lint

* Fix usage log double logging data

* Fix test

---------

Co-authored-by: Theodore Li <teddy@zenobiapay.com>
2026-03-07 13:06:57 -05:00
Waleed
1ba1bc8edb feat(evernote): add Evernote integration with 11 tools (#3456)
* feat(evernote): add Evernote integration with 11 tools

* fix(evernote): fix signed integer mismatch in Thrift version check

* fix(evernote): fix exception field mapping and add sandbox support

* fix(evernote): address PR review feedback

* fix(evernote): clamp maxNotes to Evernote's 250 limit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:52:57 -08:00
Waleed
53fd92a30a feat(obsidian): add Obsidian integration with 15 tools (#3455)
* feat(obsidian): add Obsidian integration with 15 tools

* fix(obsidian): encode path segments individually to preserve slashes

* improvement(obsidian): add type re-exports and improve output descriptions

* fix(obsidian): remove unreachable 404 handling from transformResponse
2026-03-06 23:13:47 -08:00
Waleed
0a52b09deb feat(jira): add search_users tool for user lookup by email (#3451)
* feat(jira): add search_users tool for user lookup by email

* improvement(jira): reuse shared transformUser utility in search_users

* improvement(jira): add pagination fields to search_users response

* update

* fix(jira): filter falsy entries before transforming search_users results

* fix(jira): add defensive fallback for nullable transformUser in search_users

* fix(jira): align search_users response type with transformUser return type
2026-03-06 19:52:37 -08:00
Vikhyath Mondreti
1d36b80172 improvement(selectors): remove dead semantic fallback code (#3454)
* improvement(selectors): simplify selectorContext + add tests

* fix resolve values fallback

* another workflowid pass through

* remove dead code

* make workspace id required
2026-03-06 19:38:57 -08:00
Vikhyath Mondreti
e6a5e7f4e4 improvement(selectors): simplify selector context + add tests (#3453)
* improvement(selectors): simplify selectorContext + add tests

* fix resolve values fallback

* another workflowid pass through
2026-03-06 18:30:46 -08:00
Waleed
a71304200e improvement(oauth): centralize scopes and remove dead scope evaluation code (#3449)
* improvement(oauth): centralize scopes and remove dead scope evaluation code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(oauth): fix stale scope-descriptions.ts references and add test coverage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 17:08:25 -08:00
Vikhyath Mondreti
a4d581c76f improvement(canonical): backfill for canonical modes on config changes (#3447)
* improvement(canonical): backfill for canonical modes on config changes

* persist data changes to db
2026-03-06 16:17:14 -08:00
Waleed
f1efc598d1 fix(selectors): resolve env var references at design time for selector context (#3446)
* fix(selectors): resolve env var references at design time for selector context

Selectors now resolve {{ENV_VAR}} references before building context and
returning dependency values to consumers, enabling env-var-based credentials
(e.g. {{SLACK_BOT_TOKEN}}) to work with selector dropdowns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): prevent unresolved env var templates from leaking into context

- Fall back to undefined instead of raw template string when env var is
  missing from store, so the null-check in the context loop discards it
- Use resolvedDetailId in query cache key so React Query refetches when
  the underlying env var value changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): use || for consistent empty-string env var handling

Align use-selector-setup.ts with use-selector-query.ts by using || instead
of ?? so empty-string env var values are treated as unset.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:53:00 -08:00
Waleed
244cf4ff7e feat(selectors): add dropdown selectors for 14 integrations (#3433)
* feat(selectors): add dropdown selectors for 14 integrations

* fix(selectors): secure OAuth tokens in JSM and Confluence selector routes

Convert JSM selector-servicedesks, selector-requesttypes, and Confluence
selector-spaces routes from GET (with access token in URL query params) to
POST with authorizeCredentialUse + refreshAccessTokenIfNeeded pattern. Also
adds missing ensureCredential guard to microsoft.planner.plans registry entry.

* fix(selectors): use sanitized serviceDeskId and encode SharePoint siteId

Use serviceDeskIdValidation.sanitized instead of raw serviceDeskId in JSM
request types URL. Add encodeURIComponent to SharePoint siteId to prevent
URL path injection.

* lint

* fix(selectors): revert encodeURIComponent on SharePoint siteId

SharePoint site IDs use the format "hostname,guid,guid" with commas that
must remain unencoded for the Microsoft Graph API. The encodeURIComponent
call would convert commas to %2C and break the API call.

* fix(selectors): use sanitized cloudId in Confluence and JSM route URLs

Use cloudIdValidation.sanitized instead of raw cloudId in URL construction
for consistency with the validation pattern, even though the current
validator returns the input unchanged.

* fix(selectors): add missing context fields to resolution, ensureCredential to sharepoint.lists, and siteId validation

- Add baseId, datasetId, serviceDeskId to SelectorResolutionArgs,
  ExtendedSelectorContext, extractExtendedContext, useSelectorDisplayName,
  and resolveSelectorForSubBlock so cascading selectors resolve correctly
  through the resolution path.
- Add ensureCredential guard to sharepoint.lists registry entry.
- Add regex validation for SharePoint siteId format (hostname,GUID,GUID).

* fix(selectors): rename advanced subBlock IDs to avoid canonicalParamId clashes

Rename all advanced-mode subBlock IDs that matched their canonicalParamId
to use a `manual*` prefix, following the established convention
(e.g., manualSiteId, manualCredential). This prevents ambiguity between
subBlock IDs and canonical parameter names in the serialization layer.

25 renames across 14 blocks: baseId→manualBaseId, tableId→manualTableId,
workspace→manualWorkspace, objectType→manualObjectType, etc.

* Revert "fix(selectors): rename advanced subBlock IDs to avoid canonicalParamId clashes"

This reverts commit 4e30161c68.

* fix(selectors): rename canonicalParamIds to avoid subBlock ID clashes

Prefix all clashing canonicalParamId values with `selected_` so they
don't match any subBlock ID. Update each block's `inputs` section and
`tools.config.params` function to destructure the new canonical names
and remap them to the original tool param names. SubBlock IDs and tool
definitions remain unchanged for backwards compatibility.

Affected: 25 canonical params across 14 blocks (airtable, asana, attio,
calcom, confluence, google_bigquery, google_tasks, jsm, microsoft_planner,
notion, pipedrive, sharepoint, trello, zoom).

* fix(selectors): rename pre-existing driveId and files canonicalParamIds in SharePoint

Apply the same selected_ prefix convention to the pre-existing SharePoint
driveId and files canonical params that clashed with their subBlock IDs.

* style: format long lines in calcom, pipedrive, and sharepoint blocks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): resolve cascading context for selected_ canonical params and normalize Asana response

Strip `selected_` prefix from canonical param IDs when mapping to
SelectorContext fields so cascading selectors (Airtable base→table,
BigQuery dataset→table, JSM serviceDesk→requestType) correctly
propagate parent values.

Normalize Asana workspaces route to return `{ id, name }` instead of
`{ gid, name }` for consistency with all other selector routes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): replace hacky prefix stripping with explicit CANONICAL_TO_CONTEXT mapping

Replace CONTEXT_FIELD_SET (Record<string, true>) with CANONICAL_TO_CONTEXT
(Record<string, keyof SelectorContext>) that explicitly maps canonical
param IDs to their SelectorContext field names.

This properly handles the selected_ prefix aliases (e.g. selected_baseId
→ baseId) without string manipulation, and removes the unsafe
Record<string, unknown> cast.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(selectors): remove unnecessary selected_ prefix from canonicalParamIds

The selected_ prefix was added to avoid a perceived clash between
canonicalParamId and subBlock id values, but this clash does not
actually cause any issues — pre-existing blocks on main (Google Sheets,
Webflow, SharePoint) already use matching values successfully.

Remove the prefix from all 14 blocks, revert use-selector-setup.ts to
the simple CONTEXT_FIELD_SET pattern, and simplify tools.config.params
functions that were only remapping the prefix back.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): add spaceId selector pair to Confluence V2 block

The V2 block was missing the spaceSelector basic-mode selector that the
V1 (Legacy) block already had.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(selectors): revert V1 block changes, add selectors to Notion V1 for V2 inheritance

Confluence V1: reverted to main state (V2 has its own subBlocks).
Notion V1: added selector pairs per-operation since V2 inherits
subBlocks, inputs, and params from V1.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): audit fixes for auth patterns, registry gaps, and display name resolution

- Convert Microsoft Planner plans/tasks routes from GET+getSession to POST+authorizeCredentialUse
- Add fetchById to microsoft.planner (tasks) and sharepoint.sites registry entries
- Add ensureCredential to sharepoint.sites and microsoft.planner registry fetchList
- Update microsoft.planner.plans registry to use POST method
- Add siteId, collectionId, spreadsheetId, fileId to SelectorDisplayNameArgs and caller
- Add fileId to SelectorResolutionArgs and resolution context
- Fix Zoom topicUpdate visibility in basic mode (remove mode:'advanced')
- Change Zoom meetings selector to fetch upcoming_meetings instead of only scheduled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: lint formatting fixes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): consolidate Notion canonical param pairs into array conditions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): add missing selectorKey to Confluence V1 page selector

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): use sanitized IDs in URLs, convert SharePoint routes to POST+authorizeCredentialUse

- Use planIdValidation.sanitized in MS Planner tasks fetch URL
- Convert sharepoint/lists and sharepoint/sites from GET+getSession to POST+authorizeCredentialUse
- Update registry entries to match POST pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): revert Zoom meetings type to scheduled for broader compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): add SharePoint site ID validator, fix cascading selector display name fallbacks

- Add validateSharePointSiteId to input-validation.ts
- Use validation util in SharePoint lists route instead of inline regex
- Add || fallback to selector IDs in workflow-block.tsx so cascading
  display names resolve in basic mode (baseSelector, planSelector, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): hoist requestId before try block in all selector routes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): hoist requestId before try block in Trello boards route

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): guard selector queries against unresolved variable references

Skip fetchById and context population when values are design-time
placeholders (<Block.output> or {{ENV_VAR}}) rather than real IDs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(selectors): replace hardcoded display name fallbacks with canonical-aware resolution

Use resolveDependencyValue to resolve context values for
useSelectorDisplayName, eliminating manual || getStringValue('*Selector')
fallbacks that required updating for each new selector pair.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): tighten SharePoint site ID validation to exclude underscores

SharePoint composite site IDs use hostname,guid,guid format where only
alphanumerics, periods, hyphens, and commas are valid characters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): ensure string IDs in Pipedrive/Cal.com routes, fix Trello closed board filter

Pipedrive pipelines and Cal.com event-types/schedules routes now
consistently return string IDs via String() conversion.

Trello boards route no longer filters out closed boards, preserving
them for fetchById lookups. The closed filter is applied only in the
registry's fetchList so archived boards don't appear in dropdowns
but can still be resolved by ID for display names.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): convert Zoom meeting IDs to strings for consistency

Zoom API returns numeric meeting IDs. Convert with String() to match
the string ID convention used by all other selector routes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selectors): align registry types with route string ID returns

Routes already convert numeric IDs to strings via String(), so update
the registry types (CalcomEventType, CalcomSchedule, PipedrivePipeline,
ZoomMeeting) from id: number to id: string and remove the now-redundant
String() coercions in fetchList/fetchById.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:34:28 -08:00
Waleed
ae887185a1 fix(memory): upgrade bun from 1.3.9 to 1.3.10 (#3441) 2026-03-06 11:35:46 -08:00
Waleed
06c88441f8 fix(tool-input): restore workflow input mapper visibility (#3438) 2026-03-06 05:51:27 -08:00
Waleed
127968d467 feat(slack): add views.open, views.update, views.push, views.publish tools (#3436)
* feat(slack): add views.open, views.update, views.push, views.publish tools

* feat(slack): wire view tools into slack block definition
2026-03-05 22:10:02 -08:00
Waleed
2722f0efbf feat(reddit): add 5 new tools, fix bugs, and audit all endpoints against API docs (#3434)
* feat(reddit): add 5 new tools, fix bugs, and audit all endpoints against API docs

* fix(reddit): add optional chaining, pagination wiring, and trim safety

- Add optional chaining on children?.[0] in get_posts, get_controversial,
  search, and get_comments to prevent TypeError on unexpected API responses
- Wire after/before pagination params to get_messages block operation
- Use ?? instead of || for get_comments limit to handle 0 correctly
- Add .trim() on postId in get_comments URL path

* chore(reddit): remove unused output property constants from types.ts

* fix(reddit): add HTTP error handling to GET tools

Add !response.ok guards to get_me, get_user, get_subreddit_info,
and get_messages to return success: false on non-2xx responses
instead of silently returning empty data with success: true.

* fix(reddit): add input validation and HTTP error guards

- Add validateEnum/validatePathSegment to prevent URL path traversal
- Add !response.ok guards to send_message and reply tools
- Centralize subreddit validation in normalizeSubreddit
2026-03-05 20:07:29 -08:00
Vikhyath Mondreti
4f45f705a5 improvement(snapshot): exclude sentinel in client side activation detection (#3432) 2026-03-05 17:26:09 -08:00
Vikhyath Mondreti
d640fa0852 fix(condition): execution with subflow sentinels follow-on, snapshot highlighting, duplicate terminal logs (#3429)
* fix(condition): consecutive error logging + execution dequeuing

* fix snapshot highlighting

* address minor gaps

* fix incomplete case

* remove activatedEdges path

* cleanup tests

* address greptile comments

* update tests:
2026-03-05 17:03:02 -08:00
Vikhyath Mondreti
28f8e0fd97 fix(kbs): legacy subblock id migration + CI check (#3425)
* fix(kbs): legacy subblock id migration + CI check

* cleanup migration code

* address regex inaccuracy
2026-03-05 12:38:12 -08:00
Waleed
cc38ecaf12 feat(models): add gpt-5.4 and gpt-5.4-pro model definitions (#3424)
* feat(models): add gpt-5.4 and gpt-5.4-pro model definitions

* fix(providers): update test for gpt-5.4-pro missing verbosity support
2026-03-05 11:59:52 -08:00
Waleed
0a6a2ee694 feat(slack): add new tools and user selectors (#3420)
* feat(slack): add new tools and user selectors

* fix(slack): fix download fileName param and canvas error handling

* fix(slack): use markdown format for canvas rename title_content

* fix(slack): rename channel output to channelInfo and document presence API limitation

* lint

* fix(chat): use explicit trigger type check instead of heuristic for chat guard (#3419)

* fix(chat): use explicit trigger type check instead of heuristic for chat guard

* fix(chat): remove heuristic fallback from isExecutingFromChat

Use only overrideTriggerType === 'chat' instead of also checking
for 'input' in workflowInput, which can false-positive on manual
executions with workflow input.

* fix(chat): use isExecutingFromChat variable consistently in callbacks

Replace inline overrideTriggerType !== 'chat' checks with
!isExecutingFromChat to stay consistent with the rest of the function.

* fix(slack): add missing fields to SlackChannel interface

* fix(slack): fix canvas transformResponse type mismatch

Provide required output fields on error path to match SlackCanvasResponse type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(slack): move error field to top level in canvas transformResponse

The error field belongs on ToolResponse, not inside the output object.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 22:28:10 -08:00
Waleed
8579beb199 fix(chat): use explicit trigger type check instead of heuristic for chat guard (#3419)
* fix(chat): use explicit trigger type check instead of heuristic for chat guard

* fix(chat): remove heuristic fallback from isExecutingFromChat

Use only overrideTriggerType === 'chat' instead of also checking
for 'input' in workflowInput, which can false-positive on manual
executions with workflow input.

* fix(chat): use isExecutingFromChat variable consistently in callbacks

Replace inline overrideTriggerType !== 'chat' checks with
!isExecutingFromChat to stay consistent with the rest of the function.
2026-03-04 19:05:45 -08:00
Waleed
115b4581a5 fix(editor): pass workspaceId to useCredentialName in block preview (#3418) 2026-03-04 18:15:27 -08:00
Waleed
fcdcaed00d fix(memory): add Bun.gc, stream cancellation, and unconsumed fetch drains (#3416)
* fix(memory): add Bun.gc, stream cancellation, and unconsumed fetch drains

* fix(memory): await reader.cancel() and use non-blocking Bun.gc

* fix(memory): update Bun.gc comment to match non-blocking call

* fix(memory): use response.body.cancel() instead of response.text() for drains

* fix(executor): flush TextDecoder after streaming loop for multi-byte chars

* fix(memory): use text() drain for SecureFetchResponse which lacks body property

* fix(chat): prevent premature isExecuting=false from killing chat stream

The onExecutionCompleted/Error/Cancelled callbacks were setting
isExecuting=false as soon as the server-side SSE stream completed.
For chat executions, this triggered a useEffect in chat.tsx that
cancelled the client-side stream reader before it finished consuming
buffered data — causing empty or partial chat responses.

Skip the isExecuting=false in these callbacks for chat executions
since the chat's own finally block handles cleanup after the stream
is fully consumed.

* fix(chat): remove useEffect anti-pattern that killed chat stream on state change

The effect reacted to isExecuting becoming false to clean up streams,
but this is an anti-pattern per React guidelines — using state changes
as a proxy for events. All cleanup cases are already handled by proper
event paths: stream done (processStreamingResponse), user cancel
(handleStopStreaming), component unmount (cleanup effect), and
abort/error (catch block).

* fix(servicenow): remove invalid string comparison on numeric offset param

* upgrade turborepo
2026-03-04 17:46:20 -08:00
Waleed
04fa31864b feat(servicenow): add offset and display value params to read records (#3415)
* feat(servicenow): add offset and display value params to read records

* fix(servicenow): address greptile review feedback for offset and displayValue

* fix(servicenow): handle offset=0 correctly in pagination

* fix(servicenow): guard offset against empty string in URL builder
2026-03-04 17:01:31 -08:00
Waleed
6b355e9b54 fix(subflows): recurse into all descendants for lock, enable, and protection checks (#3412)
* fix(subflows): recurse into all descendants for lock, enable, and protection checks

* fix(subflows): prevent container resize on initial render and clean up code

- Add canvasReadyRef to skip container dimension recalculation during
  ReactFlow init — position changes from extent clamping fired before
  block heights are measured, causing containers to resize on page load
- Resolve globals.css merge conflict, remove global z-index overrides
  (handled via ReactFlow zIndex prop instead)
- Clean up subflow-node: hoist static helpers to module scope, remove
  unused ref, fix nested ternary readability, rename outlineColor→ringColor

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(subflows): use full ancestor-chain protection for descendant enable-toggle

The enable-toggle for descendants was checking only direct `locked` status
instead of walking the full ancestor chain via `isBlockProtected`. This meant
a block nested 2+ levels inside a locked subflow could still be toggled.
Also added TSDoc clarifying why boxShadow works for subflow ring indicators.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert(subflows): remove canvasReadyRef height-gating approach

The canvasReadyRef gating in onNodesChange didn't fully fix the
container resize-on-load issue. Reverting to address properly later.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove unintentional edge-interaction CSS from globals

Leftover from merge conflict resolution — not part of this PR's changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(editor): correct isAncestorLocked when block and ancestor both locked, restore fade-in transition

isAncestorLocked was derived from isBlockProtected which short-circuits
on block.locked, so a self-locked block inside a locked ancestor showed
"Unlock block" instead of "Ancestor container is locked". Now walks the
ancestor chain independently.

Also restores the accidentally removed transition-opacity duration-150
class on the ReactFlow container.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(subflows): use full ancestor-chain protection for top-level enable-toggle, restore edge-label z-index

The top-level block check in batchToggleEnabled used block.locked (self
only) while descendants used isBlockProtected (full ancestor chain). A
block inside a locked ancestor but not itself locked would bypass the
check. Now all three layers (store, collaborative hook, DB operations)
consistently use isBlockProtected/isDbBlockProtected at both levels.

Also restores the accidentally removed edge-labels z-index rule, bumped
from 60 to 1001 so labels render above child nodes (zIndex: 1000).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(subflows): extract isAncestorProtected utility, add cycle detection to all traversals

- Extract isAncestorProtected from utils.ts so editor.tsx doesn't
  duplicate the ancestor-chain walk. isBlockProtected now delegates to it.
- Add visited-set cycle detection to all ancestor walks
  (isBlockProtected, isAncestorProtected, isDbBlockProtected) and
  descendant searches (findAllDescendantNodes, findDbDescendants) to
  guard against corrupt parentId references.
- Document why click-catching div has no event bubbling concern
  (ReactFlow renders children as viewport siblings, not DOM children).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:51:32 -08:00
Waleed
127994f077 feat(slack): add remove reaction tool (#3414)
* feat(slack): add remove reaction tool

* lint
2026-03-04 15:28:41 -08:00
Waleed
efc1aeed70 fix(subflows): fix pointer events for nested subflow interaction (#3409)
* fix(subflows): fix pointer events for nested subflow interaction

* fix(subflows): use Tailwind class for pointer-events-none
2026-03-03 23:28:51 -08:00
Waleed
46065983f6 fix(editor): restore cursor position after tag/env-var completion in code editors (#3406)
* fix(editor): restore cursor position after tag/env-var completion in code editors

* lint

* refactor(editor): extract restoreCursorAfterInsertion helper, fix weak fallbacks

* updated

* fix(editor): replace useEffect with direct ref assignment for editorValueRef

* fix(editor): guard cursor restoration behind preview/readOnly check

Move restoreCursorAfterInsertion inside the !isPreview && !readOnly guard
so cursor position isn't computed against newValue when the textarea still
holds liveValue. Add comment documenting the cross-string index invariant
in the shared helper.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(editor): escape blockId in CSS selector with CSS.escape()

Prevents potential SyntaxError if blockId ever contains CSS special
characters when querying the textarea for cursor restoration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* perf(editor): use ref for cursor fallback to stabilize useCallback

Replace cursorPosition state in handleSubflowTagSelect's dependency
array with a cursorPositionRef. This avoids recreating the callback
on every keystroke since cursorPosition is only used as a fallback
when textareaRef.current is null.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(editor): pass cursor position explicitly from dropdowns

Instead of inferring cursor position by searching for delimiters in the
output string (which could match unrelated < or {{ in code), compute
the exact cursor position in TagDropdown and EnvVarDropdown where the
insertion range is definitively known, and pass it through onSelect.

This follows the same pattern used by CodeMirror, Monaco, and
ProseMirror: the insertion source always knows the range, so cursor
position is computed at the source rather than inferred by the consumer.

- TagDropdown/EnvVarDropdown: compute newCursorPosition, pass as 2nd arg
- restoreCursorAfterInsertion: simplified to just (textarea, position)
- code.tsx, condition-input.tsx, use-subflow-editor.ts: accept position
- Removed editorValueRef and cursorPositionRef from use-subflow-editor
  (no longer needed since dropdown computes position)
- Other consumers (native inputs) unaffected due to TS callback compat

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs(editor): fix JSDoc terminology — macrotask not microtask

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 22:10:00 -08:00
Waleed
2c79d0249f improvement(executor): support nested loops/parallels (#3398)
* feat(executor): support nested loop DAG construction and edge wiring

Wire inner loop sentinel nodes into outer loop sentinel chains so that
nested loops execute correctly. Resolves boundary-node detection to use
effective sentinel IDs for nested loops, handles loop-exit edges from
inner sentinel-end to outer sentinel-end, and recursively clears
execution state for all nested loop scopes between iterations.

NOTE: loop-in-loop nesting only; parallel nesting is not yet supported.
Made-with: Cursor

* feat(executor): add nested loop iteration context and named loop variable resolution

Introduce ParentIteration to track ancestor loop state, build a
loopParentMap during DAG construction, and propagate parent iterations
through block execution and child workflow contexts.

Extend LoopResolver to support named loop references (e.g. <loop1.index>)
and add output property resolution (<loop1.result>). Named references
use the block's display name normalized to a tag-safe identifier,
enabling blocks inside nested loops to reference any ancestor loop's
iteration state.

NOTE: loop-in-loop nesting only; parallel nesting is not yet supported.
Made-with: Cursor

* feat(terminal): propagate parent iteration context through SSE events and terminal display

Thread parentIterations through SSE block-started, block-completed, and
block-error events so the terminal can reconstruct nested loop
hierarchies. Update the entry tree builder to recursively nest inner
loop subflow nodes inside their parent iteration rows, using
parentIterations depth-stripping to support arbitrary nesting depth.

Display the block's store name for subflow container rows instead of
the generic "Loop" / "Parallel" label.

Made-with: Cursor

* feat(canvas): allow nesting subflow containers and prevent cycles

Remove the restriction that prevented subflow nodes from being dragged
into other subflow containers, enabling loop-in-loop nesting on the
canvas. Add cycle detection (isDescendantOf) to prevent a container
from being placed inside one of its own descendants.

Resize all ancestor containers when a nested child moves, collect
descendant blocks when removing from a subflow so boundary edges are
attributed correctly, and surface all ancestor loop tags in the tag
dropdown for blocks inside nested loops.

Made-with: Cursor

* feat(agent): add MCP server discovery mode for agent tool input (#3353)

* feat(agent): add MCP server discovery mode for agent tool input

* fix(tool-input): use type variant for MCP server tool count badge

* fix(mcp-dynamic-args): align label styling with standard subblock labels

* standardized inp format UI

* feat(tool-input): replace MCP server inline expand with drill-down navigation

* feat(tool-input): add chevron affordance and keyboard nav for MCP server drill-down

* fix(tool-input): handle mcp-server type in refresh, validation, badges, and usage control

* refactor(tool-validation): extract getMcpServerIssue, remove fake tool hack

* lint

* reorder dropdown

* perf(agent): parallelize MCP server tool creation with Promise.all

* fix(combobox): preserve cursor movement in search input, reset query on drilldown

* fix(combobox): route ArrowRight through handleSelect, remove redundant type guards

* fix(agent): rename mcpServers to mcpServerSelections to avoid shadowing DB import, route ArrowRight through handleSelect

* docs: update google integration docs

* fix(tool-input): reset drilldown state on tool selection to prevent stale view

* perf(agent): parallelize MCP server discovery across multiple servers

* improvement(tests): speed up unit tests by eliminating vi.resetModules anti-pattern (#3357)

* improvement(tests): speed up unit tests by eliminating vi.resetModules anti-pattern

- convert 51 test files from vi.resetModules/vi.doMock/dynamic import to vi.hoisted/vi.mock/static import
- add global @sim/db mock to vitest.setup.ts
- switch 4 test files from jsdom to node environment
- remove all vi.importActual calls that loaded heavy modules (200+ block files)
- remove slow mockConsoleLogger/mockAuth/setupCommonApiMocks helpers
- reduce real setTimeout delays in engine tests
- mock heavy transitive deps in diff-engine test

test execution time: 34s -> 9s (3.9x faster)
environment time: 2.5s -> 0.6s (4x faster)

* docs(testing): update testing best practices with performance rules

- document vi.hoisted + vi.mock + static import as the standard pattern
- explicitly ban vi.resetModules, vi.doMock, vi.importActual, mockAuth, setupCommonApiMocks
- document global mocks from vitest.setup.ts
- add mock pattern reference for auth, hybrid auth, and database chains
- add performance rules section covering heavy deps, jsdom vs node, real timers

* fix(tests): fix 4 failing test files with missing mocks

- socket/middleware/permissions: add vi.mock for @/lib/auth to prevent transitive getBaseUrl() call
- workflow-handler: add vi.mock for @/executor/utils/http matching executor mock pattern
- evaluator-handler: add db.query.account mock structure before vi.spyOn
- router-handler: same db.query.account fix as evaluator

* fix(tests): replace banned Function type with explicit callback signature

* feat(databricks): add Databricks integration with 8 tools (#3361)

* feat(databricks): add Databricks integration with 8 tools

Add complete Databricks integration supporting SQL execution, job management,
run monitoring, and cluster listing via Personal Access Token authentication.

Tools: execute_sql, list_jobs, run_job, get_run, list_runs, cancel_run,
get_run_output, list_clusters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(databricks): throw on invalid JSON params, fix boolean coercion, add expandTasks field

- Throw errors on invalid JSON in jobParameters/notebookParams instead of silently defaulting to {}
- Always set boolean params explicitly to prevent string 'false' being truthy
- Add missing expandTasks dropdown UI field for list_jobs operation

* fix(databricks): align tool inputs/outputs with official API spec

- execute_sql: fix wait_timeout default description (50s, not 10s)
- get_run: add queueDuration field, update lifecycle/result state enums
- get_run_output: fix notebook output size (5 MB not 1 MB), add logsTruncated field
- list_runs: add userCancelledOrTimedout to state, fix limit range (1-24), update state enums
- list_jobs: fix name filter description to "exact case-insensitive"
- list_clusters: add PIPELINE_MAINTENANCE to ClusterSource enum

* fix(databricks): regenerate docs to reflect API spec fixes

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat(luma): add Luma integration for event and guest management (#3364)

* feat(luma): add Luma integration for event and guest management

Add complete Luma (lu.ma) integration with 6 tools: get event, create event,
update event, list calendar events, get guests, and add guests. Includes block
configuration with wandConfig for timestamps/timezones/durations, advanced mode
for optional fields, and generated documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(luma): address PR review feedback

- Remove hosts field from list_events transformResponse (not in LumaEventEntry type)
- Fix truncated add_guests description by removing quotes that broke docs generator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(luma): fix update_event field name and add_guests response parsing

- Use 'id' instead of 'event_id' in update_event request body per API spec
- Fix add_guests to parse entries[].guest response structure instead of flat guests array

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat(gamma): add gamma integration for AI-powered content generation (#3358)

* feat(gamma): add gamma integration for AI-powered content generation

* fix(gamma): address PR review comments

- Make credits/error conditionally included in check_status response to avoid always-truthy objects
- Replace full wordmark SVG with square "G" letterform for proper rendering in icon slots

* fix(gamma): remove imageSource from generate_from_template endpoint

The from-template API only accepts imageOptions.model and imageOptions.style,
not imageOptions.source (image source is inherited from the template).

* fix(gamma): use typed output in check_status transformResponse

* regen docs

* feat(greenhouse): add greenhouse integration for managing candidates, jobs, and applications (#3363)

* feat(ashby): add ashby integration for candidate, job, and application management (#3362)

* feat(ashby): add ashby integration for candidate, job, and application management

* fix(ashby): auto-fix lint formatting in docs files

* improvement(oauth): reordered oauth modal (#3368)

* feat(loops): add Loops email platform integration (#3359)

* feat(loops): add Loops email platform integration

Add complete Loops integration with 10 tools covering all API endpoints:
- Contact management: create, update, find, delete
- Email: send transactional emails with attachments
- Events: trigger automated email sequences
- Lists: list mailing lists and transactional email templates
- Properties: create and list contact properties

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ran litn

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat(resend): expand integration with contacts, domains, and enhanced email ops (#3366)

* improvement(blocks): update luma styling and linkup field modes (#3370)

* improvement(blocks): update luma styling and linkup field modes

* improvement(fireflies): move optional fields to advanced mode

* improvement(blocks): move optional fields to advanced mode for 10 integrations

* improvement(blocks): move optional fields to advanced mode for 6 more integrations

* feat(x): add 28 new X API v2 tool integrations and expand OAuth scopes (#3365)

* feat(x): add 28 new X API v2 tool integrations and expand OAuth scopes

* fix(x): add missing nextToken param to search tweets and fix XCreateTweetParams type

* fix(x): correct API spec issues in retweeted_by, quote_tweets, personalized_trends, and usage tools

* fix(x): add missing newestId and oldestId to error meta in get_liked_tweets and get_quote_tweets

* fix(x): add missing newestId/oldestId to get_liked_tweets success branch and includes to XTweetListResponse

* fix(x): add error handling to create_tweet and delete_tweet transformResponse

* fix(x): add error handling and logger to all X tools

* fix(x): revert block requiredScopes to match current operations

* feat(x): update block to support all 28 new X API v2 tools

* fix(x): add missing text output and fix hiddenResult output key mismatch

* docs(x): regenerate docs for all 28 new X API v2 tools

* improvement(docs): audit and standardize tool description sections, update developer count to 70k (#3371)

* improvement(x): align OAuth scopes, add scope descriptions, and set optional fields to advanced mode (#3372)

* improvement(x): align OAuth scopes, add scope descriptions, and set optional fields to advanced mode

* improvement(skills): add typed JSON outputs guidance to add-tools, add-block, and add-integration skills

* improvement(skills): add final validation steps to add-tools, add-block, and add-integration skills

* fix(skills): correct misleading JSON array comment in wandConfig example

* feat(skills): add validate-integration skill for auditing tools, blocks, and registry against API docs

* improvement(skills): expand validate-integration with full block-tool alignment, OAuth scopes, pagination, and error handling checks

* improvement(ci): add sticky disk caches and bump runner for faster builds (#3373)

* improvement(selectors): make selectorKeys declarative (#3374)

* fix(webflow): resolution for selectors

* remove unecessary fallback'

* fix teams selector resolution

* make selector keys declarative

* selectors fixes

* improvement(selectors): consolidate selector input logic (#3375)

* feat(google-contacts): add google contacts integration (#3340)

* feat(google-contacts): add google contacts integration

* fix(google-contacts): throw error when no update fields provided

* lint

* update icon

* improvement(google-contacts): add advanced mode, error handling, and input trimming

- Set mode: 'advanced' on optional fields (emailType, phoneType, notes, pageSize, pageToken, sortOrder)
- Add createLogger and response.ok error handling to all 6 tools
- Add .trim() on resourceName in get, update, delete URL builders

* improvement(mcp): add all MCP server tools individually instead of as single server entry (#3376)

* improvement(mcp): add all MCP server tools individually instead of as single server entry

* fix(mcp): prevent remove popover from opening inadvertently

* fix(sse): fix memory leaks in SSE stream cleanup and add memory telemetry (#3378)

* fix(sse): fix memory leaks in SSE stream cleanup and add memory telemetry

* improvement(monitoring): add SSE metering to wand, execution-stream, and a2a-message endpoints

* fix(workflow-execute): remove abort from cancel() to preserve run-on-leave behavior

* improvement(monitoring): use stable process.getActiveResourcesInfo() API

* refactor(a2a): hoist resubscribe cleanup to eliminate duplication between start() and cancel()

* style(a2a): format import line

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(wand): set guard flag on early-return decrement for consistency

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(ashby): validate ashby integration and update skill files (#3381)

* improvement(luma): expand host response fields and harden event ID inputs (#3383)

* improvement(resend): add error handling, authMode, and naming consistency (#3382)

* fix(chat-deploy): fix launch chat popup and auth persistence, clean up React anti-patterns (#3380)

* fix(chat-deploy): fix launch chat popup and auth persistence, clean up React anti-patterns

* lint

* fix(greenhouse): fix email_address query param, add .trim() to ID paths, revert onValidationChange to useEffect

* fix(chat-deploy): fix stale AuthSelector state, stabilize refetch ref, clean up copy timeout

* fix(chat-deploy): reset chatSuccess on modal open to prevent stuck state

* improvement(loops): validate loops integration and update skill files (#3384)

* improvement(loops): validate loops integration and update skill files

* loops icon color

* update databricks icon

* fix(monitoring): set MemoryTelemetry logger to INFO level for production visibility (#3386)

Production defaults to ERROR-only logging. Without this override,
memory snapshots would be silently suppressed.

* feat(integrations): add amplitude, google pagespeed insights, and pagerduty integrations (#3385)

* feat(integrations): add amplitude and google pagespeed insights integrations

* verified and regen docs

* fix icons

* fix(integrations): add pagerduty to tool and block registries

Re-add registry entries that were reverted after initial commit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* more updates

* ack comemnts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat(docs): add API reference with OpenAPI spec and auto-generated endpoint pages (#3388)

* feat(docs): add API reference with OpenAPI spec and auto-generated endpoint pages

* multiline curl

* random improvements

* cleanup

* update docs copy

* fix build

* cast

* fix builg

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>

* fix(icons): fix pagerduty icon (#3392)

* improvement(executor): audit and harden nested loop/parallel implementation

* improvement(executor): audit and harden nested loop/parallel implementation

- Replace unsafe _childWorkflowInstanceId cast with typeof type guard
- Reuse WorkflowNodeMetadata interface instead of inline type duplication
- Rename _executeCore to executeCore (private, no underscore needed)
- Add log warning when SSE callbacks are dropped beyond MAX_SSE_CHILD_DEPTH
- Remove unnecessary onStream type assertion, use StreamingExecution type
- Convert OUTPUT_PROPERTIES/KNOWN_PROPERTIES from arrays to Sets for O(1) lookup
- Add type guard in loop resolver resolveOutput before casting
- Add TSDoc to edgeCrossesLoopBoundary explaining original-ID usage
- Add TSDoc to MAX_SSE_CHILD_DEPTH constant
- Update ParentIteration TSDoc to reflect parallel nesting support
- Type usageControl as union 'auto'|'force'|'none' in buildMcpTool
- Replace (t: any) casts with typed objects in agent-handler tests
- Add type guard in builder-data convertArrayItem
- Make ctx required in clearLoopExecutionState (only caller always passes it)
- Replace Math.random() with deterministic counter in terminal tests
- Fix isWorkflowBlockType mock to actually check block types
- Add loop-in-loop and workflow block tree tests

* improvement(executor): audit fixes for nested subflow implementation

- Fix findInnermostLoopForBlock/ParallelForBlock to return deepest nested
  container instead of first Object.keys() match
- Fix isBlockInLoopOrDescendant returning false when directLoopId equals
  target (should return true)
- Add isBlockInParallelOrDescendant with recursive nested parallel checking
  to match loop resolver behavior
- Extract duplicated ~20-line iteration context building from loop/parallel
  orchestrators into shared buildContainerIterationContext utility
- Remove inline import() type references in orchestrators
- Remove dead executionOrder field from WorkflowNodeMetadata
- Remove redundant double-normalization in findParallelBoundaryNodes
- Consolidate 3 identical tree-walk helpers into generic hasMatchInTree
- Add empty-array guards for Math.min/Math.max in terminal utils
- Make KNOWN_PROPERTIES a Set in parallel resolver for consistency
- Remove no-op handleDragEnd callback from toolbar
- Remove dead result/results entries from KNOWN_PROPERTIES in loop resolver
- Add tests for buildContainerIterationContext

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* finished

* improvement(airtable): added more tools (#3396)

* fix(layout): polyfill crypto.randomUUID for non-secure HTTP contexts (#3397)

* feat(integrations): add dub.co integration (#3400)

* feat(integrations): add dub.co integration

* improvement(dub): add manual docs description and lint formatting fixes

* lint

* fix(dub): remove unsupported optional property from block outputs

* fix(memory): fix O(n²) string concatenation and unconsumed fetch response leaks (#3399)

* fix(monitoring): set MemoryTelemetry logger to INFO level for production visibility

Production defaults to ERROR-only logging. Without this override,
memory snapshots would be silently suppressed.

* fix(memory): fix O(n²) string concatenation and unconsumed fetch response leaks

* fix(tests): add text() mock to workflow-handler test fetch responses

* fix(memory): remove unused O(n²) join in onStreamChunk callback

* chore(careers): remove careers page, redirect to Ashby jobs portal (#3401)

* chore(careers): remove careers page, redirect to Ashby jobs portal

* lint

* feat(integrations): add google meet integration (#3403)

* feat(integrations): add google meet integration

* lint

* ack comments

* ack comments

* fix(terminal): deduplicate nested container entries in buildEntryTree

Filter out container-typed block rows when matching nested subflow
nodes exist, preventing nested loops/parallels from appearing twice
(once as a flat block and once as an expandable subflow).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(executor): clean up nested subflow implementation

- Fix wireSentinelEdges to use LOOP_EXIT handle for nested loop terminals
- Extract buildExecutionPipeline to deduplicate orchestrator wiring
- Replace two-phase init with constructor injection for Loop/ParallelOrchestrator
- Remove dead code: shouldExecuteLoopNode, resolveForEachItems, isLoopNode, isParallelNode, isSubflowBlockType
- Deduplicate currentItem resolution in ParallelResolver via resolveCurrentItem
- Type getDistributionItems param as SerializedParallel instead of any
- Demote verbose per-reference logger.info to logger.debug in evaluateWhileCondition
- Add loop-in-parallel wiring test in edges.test.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): update parallel resolver test to use distribution instead of distributionItems

The distributionItems fallback was never part of SerializedParallel — it
only worked through any typing. Updated the test to use the real
distribution property.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(executor): skip loop back-edges in parallel boundary detection and update test

findParallelBoundaryNodes now skips LOOP_CONTINUE back-edges when
detecting terminal nodes, matching findLoopBoundaryNodes behavior.
Without this, a nested loop's back-edge was incorrectly counted as a
forward edge within the parallel, preventing terminal detection.

Also updated parallel resolver test to use the real distribution
property instead of the non-existent distributionItems fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(executor): clean up cloned loop scopes in deleteParallelScopeAndClones

When a parallel contains a nested loop, cloned loop scopes (__obranch-N)
created by expandParallel were not being deleted, causing stale scopes to
persist across outer loop iterations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(executor): remove dead fallbacks, fix nested loop boundary detection, restore executionOrder

- Remove unreachable `?? candidateIds[0]` fallbacks in loop/parallel resolvers
- Remove arbitrary first-match fallback scan in findEffectiveContainerId
- Fix edgeCrossesLoopBoundary to use innermost loop detection for nested loops
- Add warning log for missing branch outputs in parallel aggregation
- Restore executionOrder on WorkflowNodeMetadata and pipe through child workflow notification
- Remove dead sim-drag-subflow classList.remove call
- Clean up cloned loop subflowParentMap entries in deleteParallelScopeAndClones

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* leftover

* upgrade turborepo

* update stagehand icon

* fix(tag-dropdown): show contextual loop/parallel tags for deeply nested blocks

findAncestorLoops only checked direct loop membership, missing blocks nested
inside parallels within loops (and vice versa). Refactored to walk through
both loop and parallel containers recursively, so a block inside a parallel
inside a loop correctly sees the loop's contextual tags (index, currentItem)
instead of the loop's output tags (results).

Also fixed parallel ancestor detection to handle nested parallel-in-loop and
loop-in-parallel scenarios, collecting all ancestor parallels instead of just
the immediate containing one.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* testing

* fixed dedicated logs

* fix

* fix(subflows): enable nested subflow interaction and execution highlighting

Remove !important z-index overrides that prevented nested subflows from
being grabbed/dragged independently. Z-index is now managed by ReactFlow's
elevateNodesOnSelect and per-node zIndex: depth props. Also adds execution
status highlighting for nested subflows in both canvas and snapshot preview.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(preview): add cycle guard to recursive subflow status derivation

Prevents infinite recursion if subflowChildrenMap contains circular
references by tracking visited nodes during traversal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Vasyl Abramovych <vasyl.abramovych@gmail.com>
2026-03-03 19:21:52 -08:00
Waleed
1cf7fdfc8c fix(logs): add status field to log detail API for polling (#3405) 2026-03-03 18:00:21 -08:00
Waleed
37bdffeda0 fix(socket): persist outbound edges from locked blocks (#3404)
* fix(socket): persist outbound edges from locked blocks

* fix(socket): align edge remove protection check with client-side behavior

* fix(socket): align batch edge protection checks with target-only model

* fix(socket): update stale comments for edge protection checks
2026-03-03 12:54:07 -08:00
Waleed
6fa4b9b410 feat(integrations): add brandfetch integration (#3402)
* feat(integrations): add brandfetch integration

* lint

* ack comments
2026-03-02 22:10:38 -08:00
Waleed
f0ee492ada feat(integrations): add google meet integration (#3403)
* feat(integrations): add google meet integration

* lint

* ack comments
2026-03-02 21:59:09 -08:00
Waleed
a8e0203a92 chore(careers): remove careers page, redirect to Ashby jobs portal (#3401)
* chore(careers): remove careers page, redirect to Ashby jobs portal

* lint
2026-03-02 14:12:03 -08:00
Waleed
ebb9a2bdd3 fix(memory): fix O(n²) string concatenation and unconsumed fetch response leaks (#3399)
* fix(monitoring): set MemoryTelemetry logger to INFO level for production visibility

Production defaults to ERROR-only logging. Without this override,
memory snapshots would be silently suppressed.

* fix(memory): fix O(n²) string concatenation and unconsumed fetch response leaks

* fix(tests): add text() mock to workflow-handler test fetch responses

* fix(memory): remove unused O(n²) join in onStreamChunk callback
2026-03-02 13:58:03 -08:00
Waleed
61a447aba5 feat(integrations): add dub.co integration (#3400)
* feat(integrations): add dub.co integration

* improvement(dub): add manual docs description and lint formatting fixes

* lint

* fix(dub): remove unsupported optional property from block outputs
2026-03-02 13:45:09 -08:00
Waleed
e91ab6260a fix(layout): polyfill crypto.randomUUID for non-secure HTTP contexts (#3397) 2026-03-02 11:57:31 -08:00
Waleed
afaa361801 improvement(airtable): added more tools (#3396) 2026-03-02 10:58:21 -08:00
Waleed
cd88706ea4 fix(icons): fix pagerduty icon (#3392) 2026-03-01 23:43:09 -08:00
Waleed
79bb4e5ad8 feat(docs): add API reference with OpenAPI spec and auto-generated endpoint pages (#3388)
* feat(docs): add API reference with OpenAPI spec and auto-generated endpoint pages

* multiline curl

* random improvements

* cleanup

* update docs copy

* fix build

* cast

* fix builg

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
2026-03-01 22:53:18 -08:00
Waleed
ee20e119de feat(integrations): add amplitude, google pagespeed insights, and pagerduty integrations (#3385)
* feat(integrations): add amplitude and google pagespeed insights integrations

* verified and regen docs

* fix icons

* fix(integrations): add pagerduty to tool and block registries

Re-add registry entries that were reverted after initial commit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* more updates

* ack comemnts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 18:56:34 -08:00
Waleed
3788660366 fix(monitoring): set MemoryTelemetry logger to INFO level for production visibility (#3386)
Production defaults to ERROR-only logging. Without this override,
memory snapshots would be silently suppressed.
2026-02-28 13:58:21 -08:00
Waleed
9be75e3633 improvement(loops): validate loops integration and update skill files (#3384)
* improvement(loops): validate loops integration and update skill files

* loops icon color

* update databricks icon
2026-02-28 12:37:01 -08:00
Waleed
40bab7731a fix(chat-deploy): fix launch chat popup and auth persistence, clean up React anti-patterns (#3380)
* fix(chat-deploy): fix launch chat popup and auth persistence, clean up React anti-patterns

* lint

* fix(greenhouse): fix email_address query param, add .trim() to ID paths, revert onValidationChange to useEffect

* fix(chat-deploy): fix stale AuthSelector state, stabilize refetch ref, clean up copy timeout

* fix(chat-deploy): reset chatSuccess on modal open to prevent stuck state
2026-02-28 12:01:42 -08:00
Waleed
96096e0ad1 improvement(resend): add error handling, authMode, and naming consistency (#3382) 2026-02-28 11:19:42 -08:00
Waleed
647a3eb05b improvement(luma): expand host response fields and harden event ID inputs (#3383) 2026-02-28 11:19:24 -08:00
Waleed
0195a4cd18 improvement(ashby): validate ashby integration and update skill files (#3381) 2026-02-28 11:16:40 -08:00
Waleed
b42f80e8ab fix(sse): fix memory leaks in SSE stream cleanup and add memory telemetry (#3378)
* fix(sse): fix memory leaks in SSE stream cleanup and add memory telemetry

* improvement(monitoring): add SSE metering to wand, execution-stream, and a2a-message endpoints

* fix(workflow-execute): remove abort from cancel() to preserve run-on-leave behavior

* improvement(monitoring): use stable process.getActiveResourcesInfo() API

* refactor(a2a): hoist resubscribe cleanup to eliminate duplication between start() and cancel()

* style(a2a): format import line

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(wand): set guard flag on early-return decrement for consistency

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 10:37:07 -08:00
Waleed
38ac86c4fd improvement(mcp): add all MCP server tools individually instead of as single server entry (#3376)
* improvement(mcp): add all MCP server tools individually instead of as single server entry

* fix(mcp): prevent remove popover from opening inadvertently
2026-02-27 14:02:11 -08:00
Waleed
4cfe8be75a feat(google-contacts): add google contacts integration (#3340)
* feat(google-contacts): add google contacts integration

* fix(google-contacts): throw error when no update fields provided

* lint

* update icon

* improvement(google-contacts): add advanced mode, error handling, and input trimming

- Set mode: 'advanced' on optional fields (emailType, phoneType, notes, pageSize, pageToken, sortOrder)
- Add createLogger and response.ok error handling to all 6 tools
- Add .trim() on resourceName in get, update, delete URL builders
2026-02-27 10:55:51 -08:00
Vikhyath Mondreti
49db3ca50b improvement(selectors): consolidate selector input logic (#3375) 2026-02-27 10:18:25 -08:00
Vikhyath Mondreti
e3ff595a84 improvement(selectors): make selectorKeys declarative (#3374)
* fix(webflow): resolution for selectors

* remove unecessary fallback'

* fix teams selector resolution

* make selector keys declarative

* selectors fixes
2026-02-27 07:56:35 -08:00
Waleed
b3424e2047 improvement(ci): add sticky disk caches and bump runner for faster builds (#3373) 2026-02-27 00:12:36 -08:00
Waleed
71ecf6c82e improvement(x): align OAuth scopes, add scope descriptions, and set optional fields to advanced mode (#3372)
* improvement(x): align OAuth scopes, add scope descriptions, and set optional fields to advanced mode

* improvement(skills): add typed JSON outputs guidance to add-tools, add-block, and add-integration skills

* improvement(skills): add final validation steps to add-tools, add-block, and add-integration skills

* fix(skills): correct misleading JSON array comment in wandConfig example

* feat(skills): add validate-integration skill for auditing tools, blocks, and registry against API docs

* improvement(skills): expand validate-integration with full block-tool alignment, OAuth scopes, pagination, and error handling checks
2026-02-26 23:30:24 -08:00
Waleed
e9e5ba2c5b improvement(docs): audit and standardize tool description sections, update developer count to 70k (#3371) 2026-02-26 23:02:58 -08:00
Waleed
9233d4ebc9 feat(x): add 28 new X API v2 tool integrations and expand OAuth scopes (#3365)
* feat(x): add 28 new X API v2 tool integrations and expand OAuth scopes

* fix(x): add missing nextToken param to search tweets and fix XCreateTweetParams type

* fix(x): correct API spec issues in retweeted_by, quote_tweets, personalized_trends, and usage tools

* fix(x): add missing newestId and oldestId to error meta in get_liked_tweets and get_quote_tweets

* fix(x): add missing newestId/oldestId to get_liked_tweets success branch and includes to XTweetListResponse

* fix(x): add error handling to create_tweet and delete_tweet transformResponse

* fix(x): add error handling and logger to all X tools

* fix(x): revert block requiredScopes to match current operations

* feat(x): update block to support all 28 new X API v2 tools

* fix(x): add missing text output and fix hiddenResult output key mismatch

* docs(x): regenerate docs for all 28 new X API v2 tools
2026-02-26 22:40:57 -08:00
Waleed
78901ef517 improvement(blocks): update luma styling and linkup field modes (#3370)
* improvement(blocks): update luma styling and linkup field modes

* improvement(fireflies): move optional fields to advanced mode

* improvement(blocks): move optional fields to advanced mode for 10 integrations

* improvement(blocks): move optional fields to advanced mode for 6 more integrations
2026-02-26 22:27:58 -08:00
Waleed
47fef540cc feat(resend): expand integration with contacts, domains, and enhanced email ops (#3366) 2026-02-26 22:12:48 -08:00
Waleed
f193e9ebbc feat(loops): add Loops email platform integration (#3359)
* feat(loops): add Loops email platform integration

Add complete Loops integration with 10 tools covering all API endpoints:
- Contact management: create, update, find, delete
- Email: send transactional emails with attachments
- Events: trigger automated email sequences
- Lists: list mailing lists and transactional email templates
- Properties: create and list contact properties

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ran litn

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:09:02 -08:00
Waleed
c0f22d7722 improvement(oauth): reordered oauth modal (#3368) 2026-02-26 19:43:59 -08:00
Waleed
bf0e25c9d0 feat(ashby): add ashby integration for candidate, job, and application management (#3362)
* feat(ashby): add ashby integration for candidate, job, and application management

* fix(ashby): auto-fix lint formatting in docs files
2026-02-26 19:10:06 -08:00
Waleed
d4f8ac8107 feat(greenhouse): add greenhouse integration for managing candidates, jobs, and applications (#3363) 2026-02-26 19:09:03 -08:00
Waleed
63fa938dd7 feat(gamma): add gamma integration for AI-powered content generation (#3358)
* feat(gamma): add gamma integration for AI-powered content generation

* fix(gamma): address PR review comments

- Make credits/error conditionally included in check_status response to avoid always-truthy objects
- Replace full wordmark SVG with square "G" letterform for proper rendering in icon slots

* fix(gamma): remove imageSource from generate_from_template endpoint

The from-template API only accepts imageOptions.model and imageOptions.style,
not imageOptions.source (image source is inherited from the template).

* fix(gamma): use typed output in check_status transformResponse

* regen docs
2026-02-26 19:08:46 -08:00
Waleed
50b882a3ad feat(luma): add Luma integration for event and guest management (#3364)
* feat(luma): add Luma integration for event and guest management

Add complete Luma (lu.ma) integration with 6 tools: get event, create event,
update event, list calendar events, get guests, and add guests. Includes block
configuration with wandConfig for timestamps/timezones/durations, advanced mode
for optional fields, and generated documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(luma): address PR review feedback

- Remove hosts field from list_events transformResponse (not in LumaEventEntry type)
- Fix truncated add_guests description by removing quotes that broke docs generator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(luma): fix update_event field name and add_guests response parsing

- Use 'id' instead of 'event_id' in update_event request body per API spec
- Fix add_guests to parse entries[].guest response structure instead of flat guests array

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 19:08:20 -08:00
Waleed
c8a0b62a9c feat(databricks): add Databricks integration with 8 tools (#3361)
* feat(databricks): add Databricks integration with 8 tools

Add complete Databricks integration supporting SQL execution, job management,
run monitoring, and cluster listing via Personal Access Token authentication.

Tools: execute_sql, list_jobs, run_job, get_run, list_runs, cancel_run,
get_run_output, list_clusters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(databricks): throw on invalid JSON params, fix boolean coercion, add expandTasks field

- Throw errors on invalid JSON in jobParameters/notebookParams instead of silently defaulting to {}
- Always set boolean params explicitly to prevent string 'false' being truthy
- Add missing expandTasks dropdown UI field for list_jobs operation

* fix(databricks): align tool inputs/outputs with official API spec

- execute_sql: fix wait_timeout default description (50s, not 10s)
- get_run: add queueDuration field, update lifecycle/result state enums
- get_run_output: fix notebook output size (5 MB not 1 MB), add logsTruncated field
- list_runs: add userCancelledOrTimedout to state, fix limit range (1-24), update state enums
- list_jobs: fix name filter description to "exact case-insensitive"
- list_clusters: add PIPELINE_MAINTENANCE to ClusterSource enum

* fix(databricks): regenerate docs to reflect API spec fixes

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 19:05:47 -08:00
Waleed
4ccb57371b improvement(tests): speed up unit tests by eliminating vi.resetModules anti-pattern (#3357)
* improvement(tests): speed up unit tests by eliminating vi.resetModules anti-pattern

- convert 51 test files from vi.resetModules/vi.doMock/dynamic import to vi.hoisted/vi.mock/static import
- add global @sim/db mock to vitest.setup.ts
- switch 4 test files from jsdom to node environment
- remove all vi.importActual calls that loaded heavy modules (200+ block files)
- remove slow mockConsoleLogger/mockAuth/setupCommonApiMocks helpers
- reduce real setTimeout delays in engine tests
- mock heavy transitive deps in diff-engine test

test execution time: 34s -> 9s (3.9x faster)
environment time: 2.5s -> 0.6s (4x faster)

* docs(testing): update testing best practices with performance rules

- document vi.hoisted + vi.mock + static import as the standard pattern
- explicitly ban vi.resetModules, vi.doMock, vi.importActual, mockAuth, setupCommonApiMocks
- document global mocks from vitest.setup.ts
- add mock pattern reference for auth, hybrid auth, and database chains
- add performance rules section covering heavy deps, jsdom vs node, real timers

* fix(tests): fix 4 failing test files with missing mocks

- socket/middleware/permissions: add vi.mock for @/lib/auth to prevent transitive getBaseUrl() call
- workflow-handler: add vi.mock for @/executor/utils/http matching executor mock pattern
- evaluator-handler: add db.query.account mock structure before vi.spyOn
- router-handler: same db.query.account fix as evaluator

* fix(tests): replace banned Function type with explicit callback signature
2026-02-26 15:46:49 -08:00
Waleed
c6e147e56a feat(agent): add MCP server discovery mode for agent tool input (#3353)
* feat(agent): add MCP server discovery mode for agent tool input

* fix(tool-input): use type variant for MCP server tool count badge

* fix(mcp-dynamic-args): align label styling with standard subblock labels

* standardized inp format UI

* feat(tool-input): replace MCP server inline expand with drill-down navigation

* feat(tool-input): add chevron affordance and keyboard nav for MCP server drill-down

* fix(tool-input): handle mcp-server type in refresh, validation, badges, and usage control

* refactor(tool-validation): extract getMcpServerIssue, remove fake tool hack

* lint

* reorder dropdown

* perf(agent): parallelize MCP server tool creation with Promise.all

* fix(combobox): preserve cursor movement in search input, reset query on drilldown

* fix(combobox): route ArrowRight through handleSelect, remove redundant type guards

* fix(agent): rename mcpServers to mcpServerSelections to avoid shadowing DB import, route ArrowRight through handleSelect

* docs: update google integration docs

* fix(tool-input): reset drilldown state on tool selection to prevent stale view

* perf(agent): parallelize MCP server discovery across multiple servers
2026-02-26 15:17:23 -08:00
Waleed
345a95f48d fix(confluence): prevent content erasure on page/blogpost update and fix space update (#3356)
- Add body-format=storage to GET-before-PUT for page and blogpost updates
  (without this, Confluence v2 API does not return body content, causing
  the fallback to erase content when only updating the title)
- Fetch current space name when updating only description (Confluence API
  requires name on PUT, so we preserve the existing name automatically)
2026-02-26 14:52:57 -08:00
Waleed
e07963f88c chore(db): drop 8 redundant indexes and add partial index for stale execution cleanup (#3354) 2026-02-26 13:17:39 -08:00
Waleed
25c59e3e2e feat(devin): add devin integration for autonomous coding sessions (#3352)
* feat(devin): add devin integration for autonomous coding sessions

* lint

* improvement(devin): update tool names and add manual docs description

* improvement(devin): rename tool files to snake_case and regenerate docs

* regen docs

* fix(devin): remove redundant Number() conversions in tool request bodies
2026-02-26 11:57:50 -08:00
Waleed
dde098e8e5 fix: prevent raw workflowInput from overwriting coerced start block values (#3347)
buildUnifiedStartOutput and buildIntegrationTriggerOutput first populate
output with schema-coerced structuredInput values (via coerceValue), then
iterate workflowInput and unconditionally overwrite those keys with raw
strings. This causes typed values (arrays, objects, numbers, booleans)
passed to child workflows to arrive as stringified versions.

Add a structuredKeys guard so the workflowInput loop skips keys already
set by the coerced structuredInput, letting coerceValue's type-aware
parsing (JSON.parse for objects/arrays, Number() for numbers, etc.)
take effect.

Fixes #3105

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 07:13:19 -08:00
Waleed
5ae0115444 feat(sidebar): add lock/unlock to workflow registry context menu (#3350)
* feat(sidebar): add lock/unlock to workflow registry context menu

* docs(tools): add manual descriptions to google_books and table

* docs(tools): add manual descriptions to google_bigquery and google_tasks

* fix(sidebar): avoid unnecessary store subscriptions and fix mixed lock state toggle

* fix(sidebar): use getWorkflowLockToggleIds utility for lock toggle

Replaces manual pivot-sorting logic with the existing utility function,
which handles block ordering and no-op guards consistently.

* lint
2026-02-25 23:40:30 -08:00
Waleed
fbafe204e5 fix(confluence): add input validation for SSRF-flagged parameters (#3351) 2026-02-25 23:35:45 -08:00
Waleed
ba7d6ff298 fix(credential-selector): remove reserved icon space when no credential selected (#3348) 2026-02-25 22:29:35 -08:00
Waleed
40016e79a1 feat(google-tasks): add Google Tasks integration (#3342)
* feat(google-tasks): add Google Tasks integration

* fix(google-tasks): return actual taskId in delete response

* fix(google-tasks): use absolute imports and fix registry order

* fix(google-tasks): rename list-task-lists to list_task_lists for doc generator

* improvement(google-tasks): destructure task and taskList outputs with typed schemas

* ran lint

* improvement(google-tasks): add wandConfig for due date timestamp generation
2026-02-25 21:52:34 -08:00
Waleed
e4fb8b2fdd feat(bigquery): add Google BigQuery integration (#3341)
* feat(bigquery): add Google BigQuery integration

* fix(bigquery): add auth provider, fix docsLink and insertedRows count

* fix(bigquery): set pageToken visibility to user-or-llm for pagination

* fix(bigquery): use prefixed export names to avoid aliased imports

* lint

* improvement(bigquery): destructure tool outputs with structured array/object types

* lint
2026-02-25 19:31:06 -08:00
Waleed
d98545d554 fix(terminal): thread executionOrder through child workflow SSE events for loop support (#3346)
* fix(terminal): thread executionOrder through child workflow SSE events for loop support

* ran lint

* fix(terminal): render iteration children through EntryNodeRow for workflow block expansion

IterationNodeRow was rendering all children as flat BlockRow components,
ignoring nodeType. Workflow blocks inside loop iterations were never
rendered as WorkflowNodeRow, so they had no expand chevron or child tree.

* fix(terminal): add childWorkflowBlockId to matchesEntryForUpdate

Sub-executors reset executionOrderCounter, so child blocks across loop
iterations share the same blockId + executionOrder. Without checking
childWorkflowBlockId, updateConsole for iteration N overwrites entries
from iterations 0..N-1, causing all child blocks to be grouped under
the last iteration's workflow instance.
2026-02-25 19:02:44 -08:00
Waleed
fadbad4085 feat(confluence): add get user by account ID tool (#3345)
* feat(confluence): add get user by account ID tool

* feat(confluence): add missing tools for tasks, blog posts, spaces, descendants, permissions, and properties

Add 16 new Confluence operations: list/get/update tasks, update/delete blog posts,
create/update/delete spaces, get page descendants, list space permissions,
list/create/delete space properties. Includes API routes, tool definitions,
block config wiring, OAuth scopes, and generated docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(confluence): add missing OAuth scopes to auth.ts provider config

The OAuth authorization flow uses scopes from auth.ts, not oauth.ts.
The 9 new scopes were only added to oauth.ts and the block config but
not to the actual provider config in auth.ts, causing re-auth to still
return tokens without the new scopes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* lint

* fix(confluence): fix truncated get_user tool description in docs

Remove apostrophe from description that caused MDX generation to
truncate at the escape character.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(confluence): address PR review feedback

- Move get_user from GET to POST to avoid exposing access token in URL
- Add 400 validation for missing params in space-properties create/delete
- Add null check for blog post version before update to prevent TypeError

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(confluence): add missing response fields for descendants and tasks

- Add type and depth fields to page descendants (from Confluence API)
- Add body field (storage format) to task list/get/update responses

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* lint

* fix(confluence): use validatePathSegment for Atlassian account IDs

validateAlphanumericId rejects valid Atlassian account IDs that contain
colons (e.g. 557058:6b9c9931-4693-49c1-8b3a-931f1af98134). Use
validatePathSegment with a custom pattern allowing colons instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ran lint

* update mock

* upgrade turborepo

* fix(confluence): reject empty update body for space PUT

Return 400 when neither name nor description is provided for space
update, instead of sending an empty body to the Confluence API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(confluence): remove spaceId requirement for create_space and fix list_tasks pagination

- Remove create_space from spaceId condition array since creating a space
  doesn't require a space ID input
- Remove list_tasks from generic supportsCursor array so it uses its
  dedicated handler that correctly passes assignedTo and status filters
  during pagination

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ran lint

* fixed type errors

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:16:53 -08:00
Waleed
244e1ee495 feat(workflow): lock/unlock workflow from context menu and panel (#3336)
* feat(workflow): lock/unlock workflow from context menu and panel

* lint

* fix(workflow): prevent duplicate lock notifications, no-op guard, fix orphaned JSDoc

* improvement(workflow): memoize hasLockedBlocks to avoid inline recomputation

* feat(google-translate): add Google Translate integration (#3337)

* feat(google-translate): add Google Translate integration

* fix(google-translate): api key as query param, fix docsLink, rename tool file

* feat(google): add missing tools for Gmail, Drive, Sheets, and Calendar (#3338)

* feat(google): add missing tools for Gmail, Drive, Sheets, and Calendar

* fix(google-drive): remove dead transformResponse from move tool

* feat(confluence): return page content in get page version tool (#3344)

* feat(confluence): return page content in get page version tool

* lint

* feat(api): audit log read endpoints for admin and enterprise (#3343)

* feat(api): audit log read endpoints for admin and enterprise

* fix(api): address PR review — boolean coercion, cursor validation, detail scope

* ran lint

* unified list of languages for google translate

* fix(workflow): respect snapshot view for panel lock toggle, remove unused disableAdmin prop

* improvement(canvas-menu): remove lock icon from workflow lock toggle

* feat(audit): record audit log for workflow lock/unlock
2026-02-25 15:23:30 -08:00
Waleed
1f3dc52d15 feat(api): audit log read endpoints for admin and enterprise (#3343)
* feat(api): audit log read endpoints for admin and enterprise

* fix(api): address PR review — boolean coercion, cursor validation, detail scope

* ran lint
2026-02-25 13:46:37 -08:00
Waleed
f625482bcb feat(confluence): return page content in get page version tool (#3344)
* feat(confluence): return page content in get page version tool

* lint
2026-02-25 13:45:19 -08:00
Waleed
16f337f6fd feat(google): add missing tools for Gmail, Drive, Sheets, and Calendar (#3338)
* feat(google): add missing tools for Gmail, Drive, Sheets, and Calendar

* fix(google-drive): remove dead transformResponse from move tool
2026-02-25 13:38:35 -08:00
Waleed
063ec87ced feat(google-translate): add Google Translate integration (#3337)
* feat(google-translate): add Google Translate integration

* fix(google-translate): api key as query param, fix docsLink, rename tool file
2026-02-25 13:24:22 -08:00
Waleed
870d4b55c6 fix(templates): show description tagline on template cards (#3335) 2026-02-25 12:10:22 -08:00
Waleed
95304b2941 feat(google-sheets): add filter support to read operation (#3333)
* feat(google-sheets): add filter support to read operation

* ran lint
2026-02-25 11:34:12 -08:00
Waleed
8b0c47b06c chore(executor): extract shared utils and remove dead code from handlers (#3334) 2026-02-25 11:28:16 -08:00
Vikhyath Mondreti
774771fddd fix(call-chain): x-sim-via propagation for API blocks and MCP tools (#3332)
* fix(call-chain): x-sim-via propagation for API blocks and MCP tools

* addres bugbot comment
2026-02-25 08:41:54 -08:00
Waleed
43c0f5b199 feat(api): retry configuration for api block (#3329)
* fix(api): add configurable request retries

The API block docs described automatic retries, but the block didn't expose any retry controls and requests were executed only once.

This adds tool-level retry support with exponential backoff (including Retry-After support) for timeouts, 429s, and 5xx responses, exposes retry settings in the API block and http_request tool, and updates the docs to match.

Fixes #3225

* remove unnecessary helpers, cleanup

* update desc

* ack comments

* ack comment

* ack

* handle timeouts

---------

Co-authored-by: Jay Prajapati <79649559+jayy-77@users.noreply.github.com>
2026-02-25 00:13:47 -08:00
Waleed
ff01825b20 docs(credentials): replace environment variables page with credentials docs (#3331) 2026-02-25 00:02:16 -08:00
Vikhyath Mondreti
58d0fda173 fix(serializer): default canonical modes construction (#3330)
* fix(serializer): default canonical modes construction

* defaults for copilot

* address bugbot comments
2026-02-24 22:05:17 -08:00
Waleed
ecdb133d1b improvement(creds): bulk paste functionality, save notification, error notif (#3328)
* improvement(creds): bulk paste functionality, save notification, error notif

* use effect anti patterns

* fix add to cursor button

* fix(attio): wrap webhook body in data object and include required filter field

* fixed and tested attio webhook lifecycle
2026-02-24 19:12:10 -08:00
Waleed
d06459f489 fix(attio): automatic webhook lifecycle management and tool fixes (#3327)
* fix(attio): use code subblock type for JSON input fields

* fix(attio): correct people name attribute format in wand prompt example

* fix(attio): improve wand prompt with correct attribute formats for all field types

* fix(attio): use array format with full_name for personal-name attribute in wand prompt

* fix(attio): use loose null checks to prevent sending null params to API

* fix(attio): add offset param and make pagination fields advanced mode

* fix(attio): remove redundant (optional) from placeholders

* fix(attio): always send required workspace_access and workspace_member_access in create list

* fix(attio): always send api_slug in create list, auto-generate from name if not provided

* fix(attio): update api slug placeholder text

* fix(tools): manage lifecycle for attio tools

* updated docs

* fix(attio): remove incorrect save button reference from setup instructions

* fix(attio): log debug message when signature verification is skipped
2026-02-24 17:30:52 -08:00
Waleed
0574427d45 fix(providers): propagate abort signal to all LLM SDK calls (#3325)
* fix(providers): propagate abort signal to all LLM SDK calls

* fix(providers): propagate abort signal to deep research interactions API

* fix(providers): clean up abort listener when sleep timer resolves
2026-02-24 14:59:02 -08:00
Emir Karabeg
8f9b859a53 improvement(credentials): ui (#3322)
* improvement(credentials): ui

* fix: credentials logic

* improvement(credentials): ui

* improvement(credentials): members UI

* improvement(secrets): ui

* fix(credentials): show error when OAuth deletion fails due to missing fields

- Add deleteError state to track and display deletion errors
- Keep confirmation dialog open when deletion fails
- Show user-friendly error message when accountId or providerId is missing
- Add loading state to delete button during deletion
- Display error message in confirmation dialog with proper styling

Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>

* ran lint

* removed worktree file

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>
Co-authored-by: Waleed Latif <walif6@gmail.com>
2026-02-24 14:48:13 -08:00
Waleed
60f9eb21bf feat(attio): add Attio CRM integration with 40 tools and 18 webhook triggers (#3324)
* feat(attio): add Attio CRM integration with 40 tools and 18 webhook triggers

* update docs

* fix(attio): use timestamp generationType for date wandConfig fields
2026-02-24 13:56:42 -08:00
Waleed
9a31c7d8ad improvement(processing): reduce redundant DB queries in execution preprocessing (#3320)
* improvement(processing): reduce redundant DB queries in execution preprocessing

* improvement(processing): add defensive ID check for prefetched workflow record

* improvement(processing): fix type safety in execution error logging

Replace `as any` cast in non-SSE error path with proper `buildTraceSpans()`
transformation, matching the SSE error path. Remove redundant `as any` cast
in preprocessing.ts where the types already align.

* improvement(processing): replace `as any` casts with proper types in logging

- logger.ts: cast JSONB cost column to `WorkflowExecutionLog['cost']` instead
  of `any` in both `completeWorkflowExecution` and `getWorkflowExecution`
- logger.ts: replace `(orgUsageBefore as any)?.toString?.()` with `String()`
  since COALESCE guarantees a non-null SQL aggregate value
- logging-session.ts: cast JSONB cost to `AccumulatedCost` (the local
  interface) instead of `any` in `loadExistingCost`

* improvement(processing): use exported HighestPrioritySubscription type in usage.ts

Replace inline `Awaited<ReturnType<typeof getHighestPrioritySubscription>>`
with the already-exported `HighestPrioritySubscription` type alias.

* improvement(processing): replace remaining `as any` casts with proper types

- preprocessing.ts: use exported `HighestPrioritySubscription` type instead
  of redeclaring via `Awaited<ReturnType<...>>`
- deploy/route.ts, status/route.ts: cast `hasWorkflowChanged` args to
  `WorkflowState` instead of `any` (JSONB + object literal narrowing)
- state/route.ts: type block sanitization and save with `BlockState` and
  `WorkflowState` instead of `any`
- search-suggestions.ts: remove 8 unnecessary `as any` casts on `'date'`
  literal that already satisfies the `Suggestion['category']` union

* fix(processing): prevent double-billing race in LoggingSession completion

When executeWorkflowCore throws, its catch block fire-and-forgets
safeCompleteWithError, then re-throws. The caller's catch block also
fire-and-forgets safeCompleteWithError on the same LoggingSession. Both
check this.completed (still false) before either's async DB write resolves,
so both proceed to completeWorkflowExecution which uses additive SQL for
billing — doubling the charged cost on every failed execution.

Fix: add a synchronous `completing` flag set immediately before the async
work begins. This blocks concurrent callers at the guard check. On failure,
the flag is reset so the safe* fallback path (completeWithCostOnlyLog) can
still attempt recovery.

* fix(processing): unblock error responses and isolate run-count failures

Remove unnecessary `await waitForCompletion()` from non-SSE and SSE error
paths where no `markAsFailed()` follows — these were blocking error responses
on log persistence for no reason. Wrap `updateWorkflowRunCounts` in its own
try/catch so a run-count DB failure cannot prevent session completion, billing,
and trace span persistence.

* improvement(processing): remove dead setupExecutor method

The method body was just a debug log with an `any` parameter — logging
now works entirely through trace spans with no executor integration.

* remove logger.debug

* fix(processing): guard completionPromise as write-once (singleton promise)

Prevent concurrent safeComplete* calls from overwriting completionPromise
with a no-op. The guard now lives at the assignment site — if a completion
is already in-flight, return its promise instead of starting a new one.
This ensures waitForCompletion() always awaits the real work.

* improvement(processing): remove empty else/catch blocks left by debug log cleanup

* fix(processing): enforce waitForCompletion inside markAsFailed to prevent completion races

Move waitForCompletion() into markAsFailed() so every call site is
automatically safe against in-flight fire-and-forget completions.
Remove the now-redundant external waitForCompletion() calls in route.ts.

* fix(processing): reset completing flag on fallback failure, clean up empty catch

- completeWithCostOnlyLog now resets this.completing = false when
  the fallback itself fails, preventing a permanently stuck session
- Use _disconnectError in MCP test-connection to signal intentional ignore

* fix(processing): restore disconnect error logging in MCP test-connection

Revert unrelated debug log removal — this file isn't part of the
processing improvements and the log aids connection leak detection.

* fix(processing): address audit findings across branch

- preprocessing.ts: use undefined (not null) for failed subscription
  fetch so getUserUsageLimit does a fresh lookup instead of silently
  falling back to free-tier limits
- deployed/route.ts: log warning on loadDeployedWorkflowState failure
  instead of silently swallowing the error
- schedule-execution.ts: remove dead successLog parameter and all
  call-site arguments left over from logger.debug cleanup
- mcp/middleware.ts: drop unused error binding in empty catch
- audit/log.ts, wand.ts: promote logger.debug to logger.warn in catch
  blocks where these are the only failure signal

* revert: undo unnecessary subscription null→undefined change

getHighestPrioritySubscription never throws (it catches internally
and returns null), so the catch block in preprocessExecution is dead
code. The null vs undefined distinction doesn't matter and the
coercions added unnecessary complexity.

* improvement(processing): remove dead try/catch around getHighestPrioritySubscription

getHighestPrioritySubscription catches internally and returns null
on error, so the wrapping try/catch was unreachable dead code.

* improvement(processing): remove dead getSnapshotByHash method

No longer called after createSnapshotWithDeduplication was refactored
to use a single upsert instead of select-then-insert.

---------
2026-02-24 11:55:59 -08:00
Jay Prajapati
9e817bc5b0 fix(auth): make DISABLE_AUTH work in web app (#3297)
Return an anonymous session using the same response envelope as Better Auth's get-session endpoint, and make the session provider tolerant to both wrapped and raw session payloads.

Fixes #2524
2026-02-24 09:52:44 -08:00
Waleed
d824ce5b07 feat(confluence): add webhook triggers for Confluence events (#3318)
* feat(confluence): add webhook triggers for Confluence events

Adds 16 Confluence triggers: page CRUD, comments, blogs, attachments,
spaces, and labels — plus a generic webhook trigger.

* feat(confluence): wire triggers into block and webhook processor

Add trigger subBlocks and triggers config to ConfluenceV2Block so
triggers appear in the UI. Add Confluence signature verification and
event filtering to the webhook processor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(confluence): align trigger outputs with actual webhook payloads

- Rewrite output builders to match real Confluence webhook payload
  structure (flat spaceKey, numeric version, actual API fields)
- Remove fabricated fields (nested space/version objects, comment.body)
- Add missing fields (creatorAccountId, lastModifierAccountId, self,
  creationDate, modificationDate, accountType)
- Add extractor functions (extractPageData, extractCommentData, etc.)
  following the same pattern as Jira
- Add formatWebhookInput handler for Confluence in utils.server.ts
  so payloads are properly destructured before reaching workflows
- Make event field matching resilient (check both event and webhookEvent)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(confluence): handle generic webhook in formatWebhookInput

The generic webhook (confluence_webhook) was falling through to
extractPageData, which only returns the page field. For a catch-all
trigger that accepts all event types, preserve all entity fields
(page, comment, blog, attachment, space, label, content).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(confluence): use payload-based filtering instead of nonexistent event field

Confluence Cloud webhooks don't include an event/webhookEvent field in the
body (unlike Jira). Replaced broken event string matching with structural
payload filtering that checks which entity key is present.

* lint

* fix(confluence): read webhookSecret instead of secret in signature verification

* fix(webhooks): read webhookSecret for jira, linear, and github signature verification

These providers define their secret subBlock with id: 'webhookSecret' but the
processor was reading providerConfig.secret which is always undefined, silently
skipping signature verification even when a secret is configured.

* fix(confluence): use event field for exact matching with entity-category fallback

Admin REST API webhooks (Settings > Webhooks) include an event field for
action-level filtering (page_created vs page_updated). Connect app webhooks
omit it, so we fall back to entity-category matching.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 23:36:43 -08:00
Waleed
9bd357f184 improvement(audit): enrich metadata across 23 audit log call sites (#3319)
* improvement(audit): enrich metadata across 23 audit log call sites

* improvement(audit): enrich metadata across 23 audit log call sites
2026-02-23 23:35:57 -08:00
Waleed
d4a014f423 feat(public-api): add env var and permission group controls to disable public API access (#3317)
Add DISABLE_PUBLIC_API / NEXT_PUBLIC_DISABLE_PUBLIC_API environment variables
and disablePublicApi permission group config option to allow self-hosted
deployments and enterprise admins to globally disable the public API toggle.

When disabled: the Access toggle is hidden in the Edit API Info modal,
the execute route blocks unauthenticated public access (401), and the
public-api PATCH route rejects enabling public API (403).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 23:03:03 -08:00
Waleed
fe34d23a98 feat(gong): add Gong integration with 18 API tools (#3316)
* feat(gong): add Gong integration with 18 API tools

* fix(gong): make toDateTime optional for list_calls, add list_trackers to workspaceId condition

* chore(gong): regenerate docs

* fix(hex): update icon color and block bgColor
2026-02-23 17:57:10 -08:00
Waleed
b8dfb4dd20 fix(copy): preserve block names when pasting into workflows without conflicts (#3315) 2026-02-23 15:42:24 -08:00
Waleed
91666491cd fix(execution): scope X-Sim-Via header to internal routes and enforce depth limit (#3313)
* feat(execution): workflow cycle detection via X-Sim-Via header

* fix(execution): scope X-Sim-Via header to internal routes and add child workflow depth validation

- Move call chain header injection from HTTP tool layer (request.ts/utils.ts)
  to tool execution layer (tools/index.ts) gated on isInternalRoute, preventing
  internal workflow IDs from leaking to external third-party APIs
- Remove cycle detection from validateCallChain — depth limit alone prevents
  infinite loops while allowing legitimate self-recursion (pagination, tree
  processing, batch splitting)
- Add validateCallChain check in workflow-handler.ts before spawning child
  executor, closing the gap where in-process child workflows skipped validation
- Remove unsafe `(params as any)._context` type bypass in request.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(execution): validate child call chain instead of parent chain

Validate childCallChain (after appending current workflow ID) rather
than ctx.callChain (parent). Prevents an off-by-one where a chain at
depth 10 could still spawn an 11th workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:19:31 -08:00
Waleed
eafbb9fef4 fix(tag-dropdown): exclude downstream blocks in loops and parallel siblings (#3312)
* fix(tag-dropdown): exclude downstream blocks in loops and parallel siblings from reference picker

* chore(serializer): remove unused computeAccessibleBlockIds method

* chore(block-path-calculator): remove unused calculateAccessibleBlocksForWorkflow method

* chore(tag-dropdown): remove no-op loop node filter

* fix(tag-dropdown): remove parallel container from accessible references in parallel branches

* chore(tag-dropdown): remove no-op starter block filter

* fix(tag-dropdown): restore parallel container in accessible references for blocks inside parallel

* fix(copilot): exclude downstream loop nodes and parallel siblings from accessible references
2026-02-23 14:21:40 -08:00
Waleed
132fef06a1 fix(redis): tighten stale TCP connection detection and add fast lease deadline (#3311)
* fix(redis): tighten stale TCP connection detection and add fast lease deadline

* revert(redis): restore original retryStrategy logging

* fix(redis): clear deadline timer after Promise.race to prevent memory leak

* fix(redis): downgrade lease fallback log to warn — unavailable is expected fallback
2026-02-23 13:22:29 -08:00
Vikhyath Mondreti
2ae814549a improvement(migration): move credential selector automigration logic to server side (#3310)
* improvement(credentials): move client side automigration to server side

* fix migration func

* fix tests

* address bugbot
2026-02-23 06:33:54 -08:00
Vikhyath Mondreti
e55d41f2ef fix(credentials): credential dependent endpoints (#3309)
* fix(dependent): credential dependent endpoints

* fix tests

* fix route to not block ws creds"

* remove faulty auth checks:

* prevent unintended cascade by depends on during migration

* address bugbot comments
2026-02-23 04:38:03 -08:00
Vikhyath Mondreti
364bb196ea feat(credentials): multiple credentials per provider (#3211)
* feat(mult-credentials): progress

* checkpoint

* make it autoselect personal secret when create secret is clicked

* improve collaborative UX

* remove add member ui for workspace secrets

* bulk entry of .env

* promote to workspace secret

* more ux improvmeent

* share with workspace for oauth

* remove new badge

* share button

* copilot + oauth name comflict

* reconnect option to connect diff account

* remove credential no access marker

* canonical credential id entry

* remove migration to prep stagin migration

* migration readded

* backfill improvements

* run lint

* fix tests

* remove unused code

* autoselect provider when connecting from block

* address bugbot comments

* remove some dead code

* more permissions stuff

* remove more unused code

* address bugbot

* add filter

* remove migration to prep migration

* fix migration

* fix migration issues

* remove migration prep merge

* readd migration

* include user tables triggers

* extract shared code

* fix

* fix tx issue

* remove migration to prep merge

* readd migration

* fix agent tool input

* agent with tool input deletion case

* fix credential subblock saving

* remove dead code

* fix tests

* address bugbot comments
2026-02-23 02:26:16 -08:00
Waleed
69ec70af13 feat(terminal): expandable child workflow blocks in console (#3306)
* feat(terminal): expandable child workflow blocks in console

* fix(terminal): cycle guard in collectWorkflowDescendants, workflow node running/canceled state

* fix(terminal): expand workflow blocks nested inside loop/parallel iterations

* fix(terminal): prevent child block mixing across loop iterations for workflow blocks

* ack PR comments, remove extranoeus logs

* feat(terminal): real-time child workflow block propagation in console

* fix(terminal): align parallel guard in WorkflowBlockHandler.getIterationContext with BlockExecutor

* fix(terminal): fire onChildWorkflowInstanceReady regardless of nodeMetadata presence

* fix(terminal): use shared isWorkflowBlockType from executor/constants
2026-02-23 00:17:44 -08:00
Waleed
687c12528b fix(parallel): correct active state pulsing and duration display for parallel subflow blocks (#3305)
* fix(executor): resolve block ID for parallel subflow active state

* fix timing for parallel block

* refactor(parallel): extract shared updateActiveBlockRefCount helper

* fix(parallel): error-sticky block run status to prevent branch success masking failure

* Revert "fix(parallel): error-sticky block run status to prevent branch success masking failure"

This reverts commit 9c087cd466.
2026-02-22 15:03:33 -08:00
Waleed
996dc96d6e fix(security): allow HTTP for localhost and loopback addresses (#3304)
* fix(security): allow localhost HTTP without weakening SSRF protections

* fix(security): remove extraneous comments and fix failing SSRF test

* fix(security): derive isLocalhost from hostname not resolved IP in validateUrlWithDNS

* fix(security): verify resolved IP is loopback when hostname is localhost in validateUrlWithDNS

---------

Co-authored-by: aayush598 <aayushgid598@gmail.com>
2026-02-22 14:58:11 -08:00
Waleed
04286fc16b fix(hex): scope param renames to their respective operations (#3295) 2026-02-21 17:53:04 -08:00
Waleed
c52f78c840 fix(models): remove retired claude-3-7-sonnet and update default models (#3292) 2026-02-21 16:44:54 -08:00
Waleed
e318bf2e65 feat(tools): added hex (#3293)
* feat(tools): added hex

* update tool names
2026-02-21 16:44:39 -08:00
Waleed
4913799a27 feat(oauth): add CIMD support for client metadata discovery (#3285)
* feat(oauth): add CIMD support for client metadata discovery

* fix(oauth): add response size limit, redirect_uri and logo_uri validation to CIMD

- Add maxResponseBytes (256KB) to prevent oversized responses
- Validate redirect_uri schemes (https/http only) and reject commas
- Validate logo_uri requires HTTPS, silently drop invalid logos

* fix(oauth): add explicit userId null for CIMD client insert

* fix(oauth): fix redirect_uri error handling, skip upsert on cache hit

- Move scheme check outside try/catch so specific error isn't swallowed
- Return fromCache flag from resolveClientMetadata to skip redundant DB writes

* fix(oauth): evict CIMD cache on upsert failure to allow retry
2026-02-21 14:38:05 -08:00
Waleed
ccb4f5956d fix(redis): prevent false rate limits and code execution failures during Redis outages (#3289) 2026-02-21 12:20:19 -08:00
Vikhyath Mondreti
2a6d4fcb96 fix(deploy): reuse subblock merge helper in use change detection hook (#3287)
* fix(workflow-changes): change detection logic divergence

* use shared helper
2026-02-21 07:57:11 -08:00
Waleed
42020c3ae2 fix(mcp): use getBaseUrl for OAuth discovery metadata URLs (#3283)
* fix(mcp): use getBaseUrl for OAuth discovery metadata URLs

* fix(mcp): remove unused request params from discovery route handlers
2026-02-21 01:57:07 -08:00
Waleed
a98463a486 fix(copilot): handle negated operation conditions in block config extraction (#3282)
* fix(copilot): handle negated operation conditions in block config extraction

* fix(copilot): simplify condition evaluation to single matchesOperation call
2026-02-20 18:08:55 -08:00
Waleed
765a481864 fix(trigger): handle Slack reaction_added/reaction_removed event payloads (#3280)
* fix(trigger): handle Slack reaction_added/reaction_removed event payloads

* fix(trigger): use oldest param for conversations.history consistency

* fix oldest param

* fix(trigger): use reactions.get API to fetch message text for thread replies
2026-02-20 17:23:06 -08:00
Waleed
a1400caea0 fix(logs): replace initialData with placeholderData to fix stale log details (#3279) 2026-02-20 17:01:52 -08:00
Waleed
2fc2e12cb2 feat(slack): added ephemeral message send tool, updated ci, updated docs (#3278)
* feat(slack): added ephemeral message send tool, updated ci, updated docs

* added block kit support

* upgrade turborepo

* added wandConfig for slack block kit

* fix generation type
2026-02-20 16:53:10 -08:00
Waleed
3fa4bb4c12 feat(auth): add OAuth 2.1 provider for MCP connector support (#3274)
* feat(auth): add OAuth 2.1 provider for MCP connector support

* fix(auth): rename redirect_u_r_ls column to redirect_urls

* chore(db): regenerate oauth migration with correct column naming

* fix(auth): reorder CORS headers and handle missing redirectURI

* fix(auth): redirect to login without stale callbackUrl on account switch

* chore: run lint

* fix(auth): override credentials header on OAuth CORS entries

* fix(auth): preserve OAuth flow when switching accounts on consent page

* fix(auth): add session and user-id checks to authorize-params endpoint

* fix(auth): add expiry check, credentials, MCP CORS, and scope in WWW-Authenticate

* feat(mcp): add tool annotations for Connectors Directory compliance
2026-02-20 15:56:15 -08:00
Waleed
1b8d666c93 fix(build): fix corrupted sticky disk cache on blacksmith (#3273) 2026-02-20 13:03:23 -08:00
Waleed
71942cb53c fix(trigger): update node version to align with main app (#3272) 2026-02-20 12:32:14 -08:00
Waleed
12534163c1 fix(tables): hide tables from sidebar and block registry (#3270)
* fix(tables): hide tables from sidebar and block registry

* fix(trigger): add isolated-vm support to trigger.dev container builds (#3269)

Scheduled workflow executions running in trigger.dev containers were
failing to spawn isolated-vm workers because the native module wasn't
available in the container. This caused loop condition evaluation to
silently fail and exit after one iteration.

- Add isolated-vm to build.external and additionalPackages in trigger config
- Include isolated-vm-worker.cjs via additionalFiles for child process spawning
- Add fallback path resolution for worker file in trigger.dev environment

* lint
2026-02-20 11:58:02 -08:00
Waleed
55920e9b03 fix(trigger): add isolated-vm support to trigger.dev container builds (#3269)
Scheduled workflow executions running in trigger.dev containers were
failing to spawn isolated-vm workers because the native module wasn't
available in the container. This caused loop condition evaluation to
silently fail and exit after one iteration.

- Add isolated-vm to build.external and additionalPackages in trigger config
- Include isolated-vm-worker.cjs via additionalFiles for child process spawning
- Add fallback path resolution for worker file in trigger.dev environment
2026-02-20 11:41:28 -08:00
Waleed
958dd64740 fix(blocks): add required constraint for serviceDeskId in JSM block (#3268)
* fix(blocks): add required constraint for serviceDeskId in JSM block

* fix(blocks): rename custom field values to request field values in JSM create request
2026-02-20 11:33:52 -08:00
Vikhyath Mondreti
68f44b8df4 improvement(resolver): resovled empty sentinel to not pass through unexecuted valid refs to text inputs (#3266) 2026-02-20 01:56:33 -08:00
Waleed
9920882dc5 fix(blocks): move type coercions from tools.config.tool to tools.config.params (#3264)
* fix(blocks): move type coercions from tools.config.tool to tools.config.params

Number() coercions in tools.config.tool ran at serialization time before
variable resolution, destroying dynamic references like <block.result.count>
by converting them to NaN/null. Moved all coercions to tools.config.params
which runs at execution time after variables are resolved.

Fixed in 15 blocks: exa, arxiv, sentry, incidentio, wikipedia, ahrefs,
posthog, elasticsearch, dropbox, hunter, lemlist, spotify, youtube, grafana,
parallel. Also added mode: 'advanced' to optional exa fields.

Closes #3258

* fix(blocks): address PR review — move remaining param mutations from tool() to params()

- Moved field mappings from tool() to params() in grafana, posthog,
  lemlist, spotify, dropbox (same dynamic reference bug)
- Fixed parallel.ts excerpts/full_content boolean logic
- Fixed parallel.ts search_queries empty case (must set undefined)
- Fixed elasticsearch.ts timeout not included when already ends with 's'
- Restored dropbox.ts tool() switch for proper default fallback

* fix(blocks): restore field renames to tool() for serialization-time validation

Field renames (e.g. personalApiKey→apiKey) must be in tool() because
validateRequiredFieldsBeforeExecution calls selectToolId()→tool() then
checks renamed field names on params. Only type coercions (Number(),
boolean) stay in params() to avoid destroying dynamic variable references.
2026-02-19 21:54:16 -08:00
Waleed
9ca5254c2b fix(audit-log): lazily resolve actor name/email when missing (#3262) 2026-02-19 16:48:43 -08:00
Waleed
d7fddb2909 feat(models): add gemini-3.1-pro-preview and update gemini-3-pro thinking levels (#3263) 2026-02-19 16:20:20 -08:00
Waleed
61c7afc19e feat(tools): added redis, upstash, algolia, and revenuecat (#3261)
* feat(tools): added redis, upstash, algolia, and revenuecat

* ack comment
2026-02-19 16:13:06 -08:00
Waleed
3c470ab0f8 fix(workflows): disallow duplicate workflow names at the same folder level (#3260) 2026-02-19 14:12:43 -08:00
Waleed
2b5e436a2a fix(snapshot): changed insert to upsert when concurrent identical child workflows are running (#3259)
* fix(snapshot): changed insert to upsert when concurrent identical child workflows are running

* fixed ci tests failing
2026-02-19 13:58:35 -08:00
Lakee Sivaraya
e24c824c9a feat(tables): added tables (#2867)
* updates

* required

* trashy table viewer

* updates

* updates

* filtering ui

* updates

* updates

* updates

* one input mode

* format

* fix lints

* improved errors

* updates

* updates

* chages

* doc strings

* breaking down file

* update comments with ai

* updates

* comments

* changes

* revert

* updates

* dedupe

* updates

* updates

* updates

* refactoring

* renames & refactors

* refactoring

* updates

* undo

* update db

* wand

* updates

* fix comments

* fixes

* simplify comments

* u[dates

* renames

* better comments

* validation

* updates

* updates

* updates

* fix sorting

* fix appearnce

* updating prompt to make it user sort

* rm

* updates

* rename

* comments

* clean comments

* simplicifcaiton

* updates

* updates

* refactor

* reduced type confusion

* undo

* rename

* undo changes

* undo

* simplify

* updates

* updates

* revert

* updates

* db updates

* type fix

* fix

* fix error handling

* updates

* docs

* docs

* updates

* rename

* dedupe

* revert

* uncook

* updates

* fix

* fix

* fix

* fix

* prepare merge

* readd migrations

* add back missed code

* migrate enrichment logic to general abstraction

* address bugbot concerns

* adhere to size limits for tables

* remove conflicting migration

* add back migrations

* fix tables auth

* fix permissive auth

* fix lint

* reran migrations

* migrate to use tanstack query for all server state

* update table-selector

* update names

* added tables to permission groups, updated subblock types

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: waleed <walif6@gmail.com>
2026-02-19 13:11:35 -08:00
Waleed
dcf81372af feat(tools): advanced fields for youtube, vercel; added cloudflare and dataverse tools (#3257)
* refactor(vercel): mark optional fields as advanced mode

Move optional/power-user fields behind the advanced toggle:
- List Deployments: project filter, target, state
- Create Deployment: project ID override, redeploy from, target
- List Projects: search
- Create/Update Project: framework, build/output/install commands
- Env Vars: variable type
- Webhooks: project IDs filter
- Checks: path, details URL
- Team Members: role filter
- All operations: team ID scope

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(youtube): mark optional params as advanced mode

Hide pagination, sort order, and filter fields behind the advanced
toggle for a cleaner default UX across all YouTube operations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* added advanced fields for vercel and youtube, added cloudflare and dataverse block

* addded desc for dataverse

* add more tools

* ack comment

* more

* ops

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 23:40:45 -08:00
Waleed
ab48787422 chore(deps): upgrade next.js from 16.1.0-canary.21 to 16.1.6 (#3254) 2026-02-18 16:25:28 -08:00
Waleed
91aa1f9a52 feat(tools): added vercel block & tools (#3252)
* feat(vercel): add complete Vercel integration with 42 API tools

Add Vercel platform management integration covering deployments, projects,
environment variables, domains, DNS records, aliases, edge configs, and
team/user management. All tools use API key authentication with Bearer tokens.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(vercel): add webhook and deployment check tools

Add 8 new Vercel API tools:
- Webhooks: list, create, delete
- Deployment Checks: create, get, list, update, rerequest

Brings total Vercel tools to 50.

* fix(vercel): expand all object and array output definitions

Expand unexpanded output types:
- get_deployment: meta and gitSource objects now have properties
- list_deployment_files: children array now has items definition
- get_team: teamRoles and teamPermissions arrays now have items

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* update icon size, update docs

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:54:49 -08:00
Waleed
2979269ac3 fix(sidebar): unify workflow and folder insertion ordering (#3250)
* fix(sidebar): unify workflow and folder insertion ordering

* ack comments

* ack comments

* ack

* ack comment

* upgrade turbo

* fix build
2026-02-18 14:41:55 -08:00
Waleed
cf28822a1c fix(shortlink): remove isHosted guard from redirects, not available at build time on ECS (#3251)
* fix(shortlink): remove isHosted guard from redirects, not available at build time on ECS

* fix(shortlink): use rewrite instead of redirect for Beluga tracking
2026-02-18 14:00:25 -08:00
Waleed
86ca984926 fix(normalization): update allowed integrations checks to be fully lowercase (#3248) 2026-02-18 12:08:03 -08:00
Emir Karabeg
e3964624ac feat(sub): hide usage limits and seats info from enterprise members (non-admin) (#3243)
- Add isEnterpriseMember and canViewUsageInfo flags to subscription permissions
- Hide UsageHeader, CreditBalance, billing date, and usage notifications from enterprise members
- Show only plan name in subscription tab for enterprise members (non-admin)
- Hide usage indicator details (amount, progress pills) from enterprise members
- Team tab already hidden via requiresTeam check in settings modal

Closes #6882

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>
2026-02-18 12:01:47 -08:00
Waleed
7c7c0fd955 feat(audit-log): add audit events for templates, billing, credentials, env, deployments, passwords (#3246)
* feat(audit-log): add audit events for templates, billing, credentials, env, deployments, passwords

* improvement(audit-log): add actorName/actorEmail to all recordAudit calls

* fix(audit-log): resolve user for password reset, add CREDENTIAL_SET_INVITATION_RESENT action

* fix(audit-log): add workspaceId to deployment activation audit

* improvement(audit-log): use better-auth callback for password reset audit, remove cast

- Move password reset audit to onPasswordReset callback in auth config
  instead of coupling to better-auth's verification table internals
- Remove ugly double-cast on workflowData.workspaceId in deployment activation

* fix(audit-log): add missing actorName/actorEmail to workflow duplicate

* improvement(audit-log): add resourceName to credential set invitation accept
2026-02-18 11:53:08 -08:00
Waleed
e37b4a926d feat(audit-log): add persistent audit log system with comprehensive route instrumentation (#3242)
* feat(audit-log): add persistent audit log system with comprehensive route instrumentation

* fix(audit-log): address PR review — nullable workspaceId, enum usage, remove redundant queries

- Make audit_log.workspace_id nullable with ON DELETE SET NULL (logs survive workspace/user deletion)
- Make audit_log.actor_id nullable with ON DELETE SET NULL
- Replace all 53 routes' string literal action/resourceType with AuditAction.X and AuditResourceType.X enums
- Fix empty workspaceId ('') → null for OAuth, form, and org routes to avoid FK violations
- Remove redundant DB queries in chat manage route (use checkChatAccess return data)
- Fix organization routes to pass workspaceId: null instead of organizationId

* fix(audit-log): replace remaining workspaceId '' fallbacks with null

* fix(audit-log): credential-set org IDs, workspace deletion FK, actorId fallback, string literal action

* reran migrations

* fix(mcp,audit): tighten env var domain bypass, add post-resolution check, form workspaceId

- Only bypass MCP domain check when env var is in hostname/authority, not path/query
- Add post-resolution validateMcpDomain call in test-connection endpoint
- Match client-side isDomainAllowed to same hostname-only bypass logic
- Return workspaceId from checkFormAccess, use in form audit logs
- Add 49 comprehensive domain-check tests covering all edge cases

* fix(mcp): stateful regex lastIndex bug, RFC 3986 authority parsing

- Remove /g flag from module-level ENV_VAR_PATTERN to avoid lastIndex state
- Create fresh regex instances per call in server-side hasEnvVarInHostname
- Fix authority extraction to terminate at /, ?, or # per RFC 3986
- Prevents bypass via https://evil.com?token={{SECRET}} (no path)
- Add test cases for query-only and fragment-only env var URLs (53 total)

* fix(audit-log): try/catch for never-throw contract, accept null actorName/Email, fix misleading action

- Wrap recordAudit body in try/catch so nanoid() or header extraction can't throw
- Accept string | null for actorName and actorEmail (session.user.name can be null)
- Normalize null -> undefined before insert to match DB column types
- Fix org members route: ORG_MEMBER_ADDED -> ORG_INVITATION_CREATED (sends invite, not adds member)

* improvement(audit-log): add resource names and specific invitation actions

* fix(audit-log): use validated chat record, add mock sync tests
2026-02-18 00:54:52 -08:00
Waleed
11f3a14c02 fix(lock): prevent socket crash when locking agent blocks (#3245) 2026-02-18 00:32:09 -08:00
Emir Karabeg
eab01e0272 fix(copilot): copilot shortcut conflict (#3219)
* fix: prevent copilot keyboard shortcuts from triggering when panel is inactive

The OptionsSelector component was capturing keyboard events (1-9 number keys and Enter)
globally on the document, causing accidental option selections when users were
interacting with other parts of the application.

This fix adds a check to only handle keyboard shortcuts when the copilot panel
is the active tab, preventing the shortcuts from interfering with other workflows.

Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>

* lint

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>
Co-authored-by: Waleed Latif <walif6@gmail.com>
2026-02-17 18:47:07 -08:00
Waleed
bbcef7ce5c feat(access-control): add ALLOWED_INTEGRATIONS env var for self-hosted block restrictions (#3238)
* feat(access-control): add ALLOWED_INTEGRATIONS env var for self-hosted block restrictions

* fix(tests): add getAllowedIntegrationsFromEnv mock to agent-handler tests

* fix(access-control): add auth to allowlist endpoint, fix loading state race, use accurate error message

* fix(access-control): remove auth from allowed-integrations endpoint to match models endpoint pattern

* fix(access-control): normalize blockType to lowercase before env allowlist check

* fix(access-control): expose merged allowedIntegrations on config to prevent bypass via direct access

* consolidate merging of allowed blocks so all callers have it by default

* normalize to lower case

* added tests

* added tests, normalize to lower case

* added safety incase userId is missing

* fix failing tests
2026-02-17 18:46:24 -08:00
Emir Karabeg
0ee52df5a7 feat(canvas): allow locked block outbound connections (#3229)
* Allow outbound connections from locked blocks to be modified

- Modified isEdgeProtected to only check target block protection
- Outbound connections (from locked blocks) can now be added/removed
- Inbound connections (to locked blocks) remain protected
- Updated notification messages and comments to reflect the change

Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>

* update notif msg

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>
Co-authored-by: waleed <walif6@gmail.com>
2026-02-17 18:16:17 -08:00
Waleed
6421b1a0ca feat(mcp): add ALLOWED_MCP_DOMAINS env var for domain allowlist (#3240)
* feat(mcp): add ALLOWED_MCP_DOMAINS env var for domain allowlist

* ack PR comments

* cleanup
2026-02-17 18:01:52 -08:00
Waleed
61a5c98717 fix(shortlink): use redirect instead of rewrite for Beluga tracking (#3239) 2026-02-17 16:27:20 -08:00
Waleed
a0afb5d03e feat(pipedrive): added sort order to endpoints that support it, upgraded turborepo (#3237)
* feat(pipedrive): added sort order to endpoints that support it

* upgraded turborepo

* fix
2026-02-17 14:58:54 -08:00
Waleed
cdacb796a8 improvement(providers): replace @ts-ignore with typed ProviderError class (#3235) 2026-02-17 14:20:31 -08:00
Waleed
3ce54147e6 fix(pagination): add missing next_page to response interfaces and operator comments (#3236) 2026-02-17 14:13:45 -08:00
Waleed
08690b2906 feat(pagination): update pagination for remaining integrations that support it (#3233)
* feat(pagination): update pagination for remaining integrations that support it

* fixed remaining

* ack comments
2026-02-17 13:34:46 -08:00
Waleed
299cc26694 improvement(lint): fix react-doctor errors and warnings (#3232)
* improvement(lint): fix react-doctor errors and warnings

* remove separators
2026-02-17 11:40:47 -08:00
Emir Karabeg
48715ff013 improvement(copilot): scrolling stickiness (#3218)
- Changed default stickinessThreshold from 100 to 30 in use-scroll-management.ts
- Removed explicit stickinessThreshold override (40) from copilot.tsx
- Both copilot and chat now use the same default value of 30
- This makes scrolling less sticky across all copilot message interactions

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>
2026-02-17 10:33:10 -08:00
Waleed
ad0d0ed1f1 feat(shortlink): add Beluga short link rewrite for hosted campaigns (#3231) 2026-02-17 10:32:32 -08:00
waleed
8ebe753bd8 Merge remote-tracking branch 'origin/main' into staging
# Conflicts:
#	apps/docs/content/docs/fr/tools/onepassword.mdx
#	apps/docs/content/docs/ja/tools/enrich.mdx
#	apps/docs/content/docs/ja/tools/google_maps.mdx
#	apps/docs/content/docs/ja/tools/onepassword.mdx
#	apps/docs/content/docs/zh/tools/enrich.mdx
#	apps/docs/content/docs/zh/tools/google_forms.mdx
#	apps/docs/content/docs/zh/tools/google_maps.mdx
#	apps/docs/content/docs/zh/tools/notion.mdx
#	apps/docs/content/docs/zh/tools/onepassword.mdx
#	apps/docs/content/docs/zh/tools/tinybird.mdx
2026-02-16 00:25:04 -08:00
Waleed
40a3962c1e fix(docs): update docs and disable i18n action, upgrade turborepo (#3227)
* feat(i18n): change lockfile (#3216)

* fix: update i18n.lock

* feat(docs): enhance documentation with new sections on file handling, form deployment, quick reference, agent skills, and A2A integration

* fix(docs): update docs and disable i18n action

* fix build

---------

Co-authored-by: cherkanov_art <45258907+cherkanovart@users.noreply.github.com>
2026-02-16 00:20:25 -08:00
cherkanov_art
d1d43b27bd feat(i18n): change lockfile (#3216)
* fix: update i18n.lock

* feat(docs): enhance documentation with new sections on file handling, form deployment, quick reference, agent skills, and A2A integration
2026-02-16 00:00:12 -08:00
Waleed
e204628a12 v0.5.90: workflow duplication improvements, model allowlist, logs performance, i18n 2026-02-15 23:42:42 -08:00
cherkanov_art
6f595f6a2c feat(i18n): change lockfile (#3216)
* fix: update i18n.lock

* feat(docs): enhance documentation with new sections on file handling, form deployment, quick reference, agent skills, and A2A integration
2026-02-15 23:41:56 -08:00
Waleed
92b4f77228 fix(logs): stabilize callbacks and memo-wrap components to eliminate re-render cascade (#3222) 2026-02-14 18:04:32 -08:00
Vikhyath Mondreti
c44211a936 fix(workflow-vars): duplicate, export/import copies (#3224) 2026-02-14 16:02:25 -08:00
Waleed
5b0532d473 refactor(tool-input): replace bidirectional effects with zustand subscription (#3215)
* refactor(tool-input): replace bidirectional effects with zustand subscription

* added wand for custom cron, fixed slack inconsistency

* fix slack
2026-02-14 11:19:51 -08:00
Waleed
3ef6b05035 fix(model): validate default model against available options 2026-02-13 15:16:20 -08:00
Waleed
b45f3962fc v0.5.89: resume execution on refresh, google books, tool input subblock improvements 2026-02-13 00:36:54 -08:00
Waleed
7fbbc7ba7a fix(tool-input): sync cleared subblock values to tool params (#3214) 2026-02-13 00:18:25 -08:00
Waleed
a337aa7dfe feat(internal): added internal api base url for internal calls (#3212)
* feat(internal): added internal api base url for internal calls

* make validation on http more lax
2026-02-12 23:56:35 -08:00
Waleed
022e84c4b1 feat(creators): added referrers, code redemption, campaign tracking, etc (#3198)
* feat(creators): added referrers, code redemption, campaign tracking, etc

* more

* added zod

* remove default

* remove duplicate index

* update admin routes

* reran migrations

* lint

* move userstats record creation inside tx

* added reason for already attributed case

* cleanup referral attributes
2026-02-12 20:07:40 -08:00
Waleed
602e371a7a refactor(tool-input): subblock-first rendering, component extraction, bug fixes (#3207)
* refactor(tool-input): eliminate SyncWrappers, add canonical toggle and dependsOn gating

Replace 17+ individual SyncWrapper components with a single centralized
ToolSubBlockRenderer that bridges the subblock store with StoredTool.params
via synthetic store keys. This reduces ~1000 lines of duplicated wrapper
code and ensures tool-input renders subblock components identically to
the standalone SubBlock path.

- Add ToolSubBlockRenderer with bidirectional store sync
- Add basic/advanced mode toggle (ArrowLeftRight) using collaborative functions
- Add dependsOn gating via useDependsOnGate (fields disable instead of hiding)
- Add paramVisibility field to SubBlockConfig for tool-input visibility control
- Pass canonicalModeOverrides through getSubBlocksForToolInput
- Show (optional) label for non-user-only fields (LLM can inject at runtime)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tool-input): restore optional indicator, fix folder selector and canonical toggle, extract components

- Attach resolved paramVisibility to subblocks from getSubBlocksForToolInput
- Add labelSuffix prop to SubBlock for "(optional)" badge on user-or-llm params
- Fix folder selector missing for tools with canonicalParamId (e.g. Google Drive)
- Fix canonical toggle not clickable by letting SubBlock handle dependsOn internally
- Extract ParameterWithLabel, ToolSubBlockRenderer, ToolCredentialSelector to components/tools/
- Extract StoredTool interface to types.ts, selection helpers to utils.ts
- Remove dead code (mcpError, refreshTools, oldParamIds, initialParams)
- Strengthen typing: replace any with proper types on icon components and evaluateParameterCondition

* add sibling values to subblock context since subblock store isn't relevant in tool input, and removed unused param

* cleanup

* fix(tool-input): render uncovered tool params alongside subblocks

The SubBlock-first rendering path was hard-returning after rendering
subblocks, so tool params without matching subblocks (like inputMapping
for workflow tools) were never rendered. Now renders subblocks first,
then any remaining displayParams not covered by subblocks via the legacy
ParameterWithLabel fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tool-input): auto-refresh workflow inputs after redeploy

After redeploying a child workflow via the stale badge, the workflow
state cache was not invalidated, so WorkflowInputMapperInput kept
showing stale input fields until page refresh. Now invalidates
workflowKeys.state on deploy success.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tool-input): correct workflow selector visibility and tighten (optional) spacing

- Set workflowId param to user-only in workflow_executor tool config
  so "Select Workflow" no longer shows "(optional)" indicator
- Tighten (optional) label spacing with -ml-[3px] to counteract
  parent Label's gap-[6px], making it feel inline with the label text

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tool-input): align (optional) text to baseline instead of center

Use items-baseline instead of items-center on Label flex containers
so the smaller (optional) text aligns with the label text baseline
rather than sitting slightly below it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tool-input): increase top padding of expanded tool body

Bump the expanded tool body container's top padding from 8px to 12px
for more breathing room between the header bar and the first parameter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tool-input): apply extra top padding only to SubBlock-first path

Revert container padding to py-[8px] (MCP tools were correct).
Wrap SubBlock-first output in a div with pt-[4px] so only registry
tools get extra breathing room from the container top.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tool-input): increase gap between SubBlock params for visual clarity

SubBlock's internal gap (10px between label and input) matched the
between-parameter gap (10px), making them indistinguishable. Increase
the between-parameter gap to 14px so consecutive parameters are
visually distinct, matching the separation seen in ParameterWithLabel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix spacing and optional tag

* update styling + move predeploy checks earlier for first time deploys

* update change detection to account for synthetic tool ids

* fix remaining blocks who had files visibility set to hidden

* cleanup

* add catch

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 19:01:04 -08:00
Theodore Li
9a06cae591 Merge pull request #3210 from simstudioai/feat/google-books
feat(google books): Add google books integration
2026-02-12 16:18:42 -08:00
Theodore Li
dce47a101c Migrate last response to types 2026-02-12 15:45:00 -08:00
Theodore Li
1130f8ddb2 Remove redundant error handling, move volume item to types file 2026-02-12 15:31:12 -08:00
Waleed
ebc2ffa1c5 fix(agent): always fetch latest custom tool from DB when customToolId is present (#3208)
* fix(agent): always fetch latest custom tool from DB when customToolId is present

* test(agent): use generic test data for customToolId resolution tests

* fix(agent): mock buildAuthHeaders in tests for CI compatibility

* remove inline mocks in favor of sim/testing ones
2026-02-12 15:31:11 -08:00
Theodore Li
fc97ce007d Correct error handling, specify auth mode as api key 2026-02-12 15:26:13 -08:00
Theodore Li
6c006cdfec feat(google books): Add google books integration 2026-02-12 15:01:33 -08:00
Siddharth Ganesan
c380e59cb3 fix(copilot): make default model opus 4.5 (#3209)
* Fix default model

* Fix
2026-02-12 13:17:45 -08:00
Waleed
2944579d21 fix(s3): support get-object region override and robust S3 URL parsing (#3206)
* fix(s3): support get-object region override and robust S3 URL parsing

* ack pr comments
2026-02-12 10:59:22 -08:00
Waleed
81dfeb0bb0 fix(terminal): reconnect to running executions after page refresh (#3200)
* fix(terminal): reconnect to running executions after page refresh

* fix(terminal): use ExecutionEvent type instead of any in reconnection stream

* fix(execution): type event buffer with ExecutionEvent instead of Record<string, unknown>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(execution): validate fromEventId query param in reconnection endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix some bugs

* fix(variables): fix tag dropdown and cursor alignment in variables block (#3199)

* feat(confluence): added list space labels, delete label, delete page prop (#3201)

* updated route

* ack comments

* fix(execution): reset execution state in reconnection cleanup to unblock re-entry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(execution): restore running entries when reconnection is interrupted by navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* done

* remove cast in ioredis types

* ack PR comments

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Siddharth Ganesan <siddharthganesan@gmail.com>
2026-02-11 19:31:29 -08:00
Waleed
01577a18b4 fix(change-detection): resolve false positive trigger block change detection (#3204) 2026-02-11 17:24:17 -08:00
Waleed
07d50f8fe1 v0.5.88: interactions api for gemini, trigger machine size increase, confluence ops 2026-02-11 15:36:55 -08:00
Vikhyath Mondreti
52aff4d60b fix build 2026-02-11 15:33:22 -08:00
Waleed
3a3bddd6f8 fix(confl): use recommended query param pattern for confluence route (#3202)
* fix(confl): use recommended query param pattern for confluence route

* use unused var
2026-02-11 14:59:26 -08:00
Waleed
639d50d6b9 feat(confluence): added list space labels, delete label, delete page prop (#3201) 2026-02-11 14:40:31 -08:00
Waleed
cec74e09c2 fix(variables): fix tag dropdown and cursor alignment in variables block (#3199) 2026-02-11 14:40:31 -08:00
Waleed
d5a756c9f2 fix(hotkeys): remove C, T, E tab-switching hotkeys (#3197) 2026-02-11 13:24:00 -08:00
Waleed
f3e994baf0 improvement(oom): increase trigger machine size (#3196) 2026-02-11 13:11:28 -08:00
Waleed
2f492cacc1 feat(providers): add Gemini Deep Research via Interactions API (#3192)
* feat(providers): add Gemini Deep Research via Interactions API

* fix(providers): hide memory UI for deep research models

* feat(providers): add multi-turn support and token logging for deep research

* fix(providers): only collect user messages as deep research input

* fix(providers): forward previousInteractionId to provider request

* fix(blocks): hide memory child fields for deep research models

* remove memory params from models that don't support it in provider requests

* update blog
2026-02-11 01:01:59 -08:00
Vikhyath Mondreti
27973953f6 v0.5.87: workflow block auth fix 2026-02-10 22:33:55 -08:00
Vikhyath Mondreti
5792e7e5f9 fix(auth): workflow system handler (#3193) 2026-02-10 22:25:48 -08:00
Waleed
50585273ce v0.5.86: server side copilot, copilot mcp, error notifications, jira outputs destructuring, slack trigger improvements 2026-02-10 21:49:58 -08:00
Waleed
3d5bd003ef fix(triggers): add copilot as a trigger type (#3191)
* fix(triggers): add copilot as a trigger type

* update color
2026-02-10 21:36:38 -08:00
Waleed
13a91113fd fix(logs): surface handled errors as info in logs (#3190)
* fix(logs): surface handled errors as info in logs

* pr comments
2026-02-10 21:21:45 -08:00
Vikhyath Mondreti
af01dce2c3 fix(terminal): subflow logs rendering (#3189) 2026-02-10 21:16:05 -08:00
Waleed
8a24b56f51 improvement(terminal): increase workflow logs limit from 1k to 5k per workflow (#3188) 2026-02-10 20:11:49 -08:00
Waleed
c471627ce1 fix(posthog): replace proxy rewrite with route handler for reliable body streaming (#3187)
* fix(posthog): replace proxy rewrite with route handler for reliable body streaming

* fix posthog
2026-02-10 20:05:38 -08:00
Waleed
f5dc180d9f fix(memory): upgrade bun from 1.3.3 to 1.3.9 (#3186) 2026-02-10 19:30:31 -08:00
Waleed
78fef22d0e fix(execution): scope execution state per workflow to prevent cross-workflow bleed (#3183)
* fix(execution): scope execution state per workflow to prevent cross-workflow bleed

* fix(execution): use validated workflowId param instead of non-null assertion in handleRunUntilBlock

* improvement(execution): use individual selectors to avoid unnecessary re-renders from unselectored store hook

* improvement(execution): use useShallow selector in workflow.tsx to avoid re-renders from lastRunPath/lastRunEdges changes
2026-02-10 18:17:50 -08:00
Waleed
6d16f216c8 improvement(mcp): improved mcp sse events notifs, update jira to handle files, fix UI issues in settings modal, fix org and workspace invitations when bundled (#3182)
* improvement(mcp): improved mcp sse events notifs, update jira to handle files, fix UI issues in settings modal, fix org and workspace invitations when bundled

* added back useMcpToolsEvents for event-driven discovery

* ack PR comments

* updated placeholder

* updated colors, error throwing in mcp modal

* ack comments

* updated error msg
2026-02-10 17:08:57 -08:00
Waleed
f8e9614c9c improvement(helm): support copilot-only deployments (#3185) 2026-02-10 17:03:11 -08:00
Siddharth Ganesan
c5dd90e79d feat(copilot): enterprise configuration (#3184)
* Copilot enterprise models

* Fix azure anthropic

* Fix

* Consolidation

* Cleanup

* Clean up code

* Fix lint

* cleanup

* Fix greptile
2026-02-10 16:37:30 -08:00
Vikhyath Mondreti
20b230d1aa improvement(schema): centralize derivation of block schemas (#3175)
* improvement(schema): centralize derivation of block schemas

* address bugbot comments

* remove unused code

* address greptile comments

* merge conflict resolution

* fix

* update tests

* address greptile comments

* make evaluator metrics only

* return base and metrics for evaluator
2026-02-10 16:31:50 -08:00
Vikhyath Mondreti
be3cdcf981 Merge pull request #3179 from simstudioai/improvement/file-download-timeouts
improvement(timeouts): files/base64 should use max timeouts + auth centralization
2026-02-10 15:57:06 -08:00
Emir Karabeg
73540e3936 feat(logs): add skill icon to trace spans (#3181)
Adds the AgentSkillsIcon to trace spans in logs when displaying the
load_skill tool. Previously, skills appeared with a default gray color.
Now they display with the proper skill icon and a purple (#8B5CF6)
background color, consistent with the skills icon used in the settings
modal and skill input components.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>
2026-02-10 12:46:21 -08:00
Waleed
e321f883b0 improvement(preview): added trigger mode context for deploy preview (#3177)
* improvement(preview): added trigger mode context for deploy preview

* use existing helper

* enhance disabled mode for subblocks

* update

* update all subblocks to allow scrolling in read only mode

* updated short and long input to match others, reverted triggerutils change
2026-02-09 20:32:30 -08:00
Waleed
8b4b3af120 fix(mcp): harden notification system against race conditions (#3168)
* fix(mcp): harden notification system against race conditions

- Guard concurrent connect() calls in connection manager with connectingServers Set
- Suppress post-disconnect notification handler firing in MCP client
- Clean up Redis event listeners in pub/sub dispose()
- Add tests for all three hardening fixes (11 new tests)

* updated tests

* plugged in new mcp event based system and create sse route to publish notifs

* ack commetns

* fix reconnect timer

* cleanup when running onClose

* fixed spacing on mcp settings tab

* keep error listeners before quiet in redis
2026-02-09 19:36:01 -08:00
Siddharth Ganesan
190f12fd77 feat(copilot): copilot mcp + server side copilot execution (#3173)
* v0

* v1

* Basic ss tes

* Ss tests

* Stuff

* Add mcp

* mcp v1

* Improvement

* Fix

* BROKEN

* Checkpoint

* Streaming

* Fix abort

* Things are broken

* Streaming seems to work but copilot is dumb

* Fix edge issue

* LUAAAA

* Fix stream buffer

* Fix lint

* Checkpoint

* Initial temp state, in the middle of a refactor

* Initial test shows diff store still working

* Tool refactor

* First cleanup pass complete - untested

* Continued cleanup

* Refactor

* Refactor complete - no testing yet

* Fix - cursor makes me sad

* Fix mcp

* Clean up mcp

* Updated mcp

* Add respond to subagents

* Fix definitions

* Add tools

* Add tools

* Add copilot mcp tracking

* Fix lint

* Fix mcp

* Fix

* Updates

* Clean up mcp

* Fix copilot mcp tool names to be sim prefixed

* Add opus 4.6

* Fix discovery tool

* Fix

* Remove logs

* Fix go side tool rendering

* Update docs

* Fix hydration

* Fix tool call resolution

* Fix

* Fix lint

* Fix superagent and autoallow integrations

* Fix always allow

* Update block

* Remove plan docs

* Fix hardcoded ff

* Fix dropped provider

* Fix lint

* Fix tests

* Fix dead messages array

* Fix discovery

* Fix run workflow

* Fix run block

* Fix run from block in copilot

* Fix lint

* Fix skip and mtb

* Fix typing

* Fix tool call

* Bump api version

* Fix bun lock

* Nuke bad files
2026-02-09 19:33:29 -08:00
Waleed
e5d30494cb fix(slack): resolve file metadata via files.info when event payload is partial (#3176) 2026-02-09 19:29:38 -08:00
Waleed
b3dbb4487f improvement(jsm): destructured outputs for jsm, jira, and added 1password integration (#3174)
* improvement(jsm): destructured outputs for jsm, jira, and added 1password integration

* update 1password to support cloud & locally hosted

* updated & tested 1pass

* added an additional wandConfig for OnePassword & jira search issues

* finished jira

* removed unused route

* updated types

* restore old outputs

* updated types
2026-02-09 19:28:34 -08:00
Waleed
622d0cad22 Merge pull request #3172 from simstudioai/fix/notifs
fix(notifications): throw notification on runtime errors, move predeploy checks to update in deploy modal
2026-02-09 11:49:58 -08:00
Vikhyath Mondreti
654cb2b407 v0.5.85: deployment improvements 2026-02-09 10:49:33 -08:00
Vikhyath Mondreti
c74922997c fix(triggers): id resolution for tools with trigger mode (#3170) 2026-02-09 10:28:34 -08:00
Emir Karabeg
4193007ab7 improvement(ui): deploy modal, terminal (#3167)
* improvement(deploy-modal): error and warning ui

* fix(ui): terminal top border render
2026-02-08 11:08:54 -08:00
Waleed
6c66521d64 v0.5.84: model request sanitization 2026-02-07 19:06:53 -08:00
Waleed
f9b885f6d5 fix(models): add request sanitization (#3165) 2026-02-07 19:04:15 -08:00
Vikhyath Mondreti
479cd347ad v0.5.83: agent skills, concurrent workers for v8s, airweave integration 2026-02-07 12:27:11 -08:00
Vikhyath Mondreti
0cb6714496 fix(rooms): cleanup edge case for 1hr ttl (#3163)
* fix(rooms): cleanup edge case for 1hr ttl

* revert feature flags

* address comments

* remove console log
2026-02-07 12:18:07 -08:00
Waleed
7b36f9257e improvement(models): reorder models dropdown (#3164) 2026-02-07 12:05:10 -08:00
Waleed
99ae5435e3 feat(models): updated model configs, updated anthropic provider to propagate errors back to user if any (#3159)
* feat(models): updated model configs, updated anthropic provider to propagate errors back to user if any

* moved max tokens to advanced

* updated model configs and testesd

* removed default in max config for output tokens

* moved more stuff to advanced mode in the agent block

* stronger typing

* move api key under model, update mistral and groq

* update openrouter, fixed serializer to allow ollama/vllm models without api key

* removed ollama handling
2026-02-06 22:35:57 -08:00
Vikhyath Mondreti
925f06add7 improvement(preview): render nested values like input format correctly in workflow execution preview (#3154)
* improvement(preview): nested workflow snapshots/preview when not executed

* improvements to resolve nested subblock values

* few more things

* add try catch

* fix fallback case

* deps
2026-02-06 22:12:40 -08:00
Vikhyath Mondreti
193b95cfec fix(auth): swap out hybrid auth in relevant callsites (#3160)
* fix(logs): execution files should always use our internal route

* correct degree of access control

* fix tests

* fix tag defs flag

* fix type check

* fix mcp tools

* make webhooks consistent

* fix ollama and vllm visibility

* remove dup test
2026-02-06 22:07:55 -08:00
Waleed
0ca25bbab6 fix(function): isolated-vm worker pool to prevent single-worker bottleneck + execution user id resolution (#3155)
* fix(executor): isolated-vm worker pool to prevent single-worker bottleneck

* chore(helm): add isolated-vm worker pool env vars to values.yaml

* fix(userid): resolution for fair scheduling

* add fallback back

* add to helm charts

* remove constant fallbacks

* fix

* address bugbot comments

* fix fallbacks

* one more bugbot comment

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-02-06 18:34:03 -08:00
Waleed
1edaf197b2 fix(azure): add azure-anthropic support to router, evaluator, copilot, and tokenization (#3158)
* fix(azure): add azure-anthropic support to router, evaluator, copilot, and tokenization

* added azure anthropic values to env

* fix(azure): make anthropic-version configurable for azure-anthropic provider

* fix(azure): thread provider credentials through guardrails and fix translate missing bedrockAccessKeyId

* updated guardrails

* ack'd PR comments

* fix(azure): unify credential passing pattern across all LLM handlers

- Pass all provider credentials unconditionally in router, evaluator (matching agent pattern)
- Remove conditional if-branching on providerId for credential fields
- Thread workspaceId through guardrails → hallucination validator for BYOK key resolution
- Remove getApiKey() from hallucination validator, let executeProviderRequest handle it
- Resolve vertex OAuth credentials in hallucination validator matching agent handler pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:26:10 -08:00
Waleed
474b1af145 improvement(ui): improved skills UI, validation, and permissions (#3156)
* improvement(ui): improved skills UI, validation, and permissions

* stronger typing for Skill interface

* added missing docs description

* ack comment
2026-02-06 13:11:56 -08:00
Ewan Tauran
1e21ec1fa3 feat(airweave): add airweave block (#3079)
* feat(icons): add Airweave icon and update registry with Airweave block and tool

* feat(icons): add Airweave icon and update icon mapping and metadata

* fix(search): update API key header from Authorization to X-API-Key for Airweave search tool

* refactor(icon-mapping): reorder icon imports for consistency and formatting improvements; update airweave block retrieval strategy description formatting; add newline at end of meta.json

* refactor(search): update visibility settings for retrieval strategy and query options to allow access for both users and LLMs
2026-02-06 11:43:06 -08:00
Waleed
71bd535d04 feat(skills): added skills to agent block (#3149)
* feat(skills): added skills to agent block

* improvement(skills): audit fixes, docs, icon, and UX polish

* fix(skills): consolidate redundant permission checks in POST and DELETE

* more friendly error for duplicate skills in the same workspace

* fix(executor):  loop sentinel-end wrongly queued (#3148)

* fix(executor):  loop sentinel-end wrongly queued

* fix nested subflow error highlighting

* fix(linear): align tool outputs, queries, and pagination with API (#3150)

* fix(linear): align tool outputs, queries, and pagination with API

* fix(linear): coerce first param to number, remove duplicate conditions, add null guard

* fix(resolver): response format and evaluator metrics in deactivated branch (#3152)

* fix(resolver): response format in deactivated branch

* add evaluator metrics too

* add child workflow id to the workflow block outputs

* cleanup typing

* feat(slack): add file attachment support to slack webhook trigger (#3151)

* feat(slack): add file attachment support to slack webhook trigger

* additional file handling

* lint

* ack comment

* fix(skills): hide skill selection when disabled, remove dead code

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
2026-02-06 11:38:38 -08:00
Waleed
a3a99eda19 v0.5.82: slack trigger files, pagination for linear, executor fixes 2026-02-06 00:41:52 -08:00
Waleed
ed5ed97c07 feat(slack): add file attachment support to slack webhook trigger (#3151)
* feat(slack): add file attachment support to slack webhook trigger

* additional file handling

* lint

* ack comment
2026-02-06 00:27:17 -08:00
Vikhyath Mondreti
65de27330e fix(resolver): response format and evaluator metrics in deactivated branch (#3152)
* fix(resolver): response format in deactivated branch

* add evaluator metrics too

* add child workflow id to the workflow block outputs

* cleanup typing
2026-02-06 00:14:43 -08:00
Waleed
c0b22a6490 fix(linear): align tool outputs, queries, and pagination with API (#3150)
* fix(linear): align tool outputs, queries, and pagination with API

* fix(linear): coerce first param to number, remove duplicate conditions, add null guard
2026-02-05 18:44:24 -08:00
Vikhyath Mondreti
9dcf92bd14 fix(executor): loop sentinel-end wrongly queued (#3148)
* fix(executor):  loop sentinel-end wrongly queued

* fix nested subflow error highlighting
2026-02-05 15:31:15 -08:00
Waleed
1a66d48add v0.5.81: traces fix, additional confluence tools, azure anthropic support, opus 4.6 2026-02-05 11:28:54 -08:00
Waleed
1d4d61a10a feat(azure): added azure anthropic, added backwards compat support for chat completions API, added opus 4.6 (#3145)
* feat(azure): added azure anthropic, added backwards compat support for chat completions API, added opus 4.6

* added max thinking level

* update tests

* ack comments

* update cql validation
2026-02-05 10:52:18 -08:00
Vikhyath Mondreti
2d7e6c9796 fix(client-exec): send correct client workflow state override (#3143) 2026-02-04 23:53:09 -08:00
Vikhyath Mondreti
ea3bab1f76 fix(inputs): canonical params + manual validations + params resolution cleanups (#3141)
* fix(onedrive): canonical param required validation

* fix onedrive

* cleanup canonical tool param resolution code

* fix type

* fix jira type checks

* remove manual validations
2026-02-04 22:58:51 -08:00
Waleed
552dc56fc3 feat(confluence): added more confluence endpoints (#3139)
* feat(confluence): added more confluence endpoints

* update license

* updated

* updated docs
2026-02-04 19:46:28 -08:00
Waleed
2147309365 fix(tracespans): update tracespans tool calls to accurately display inputs for successive identical tool calls (#3140) 2026-02-04 19:32:18 -08:00
Waleed
46822e91f3 v0.5.80: lock feature, enterprise modules, time formatting consolidation, files, UX and UI improvements, longer timeouts 2026-02-04 18:27:05 -08:00
Waleed
36ec68d93e fix(serializer): validate required fields for blocks without tools (#3137) 2026-02-04 16:47:18 -08:00
Vikhyath Mondreti
fce566cc2f fix(mistral): restore mistral configs for v2 version (#3138) 2026-02-04 16:03:04 -08:00
Siddharth Ganesan
1933e1aad5 improvement(openai): migrate to responses api (#3135)
* Migrate openai to use responses api

* Consolidate azure

* Fix streaming

* Bug fixes

* Bug fixes

* Fix responseformat

* Refactor

* Fix bugs

* Fix

* Fix azure openai response format with tool calls

* Fixes

* Fixes

* Fix temp
2026-02-04 15:50:18 -08:00
Waleed
793adda986 fix(limits): updated rate limiter to match execution timeouts, adjusted timeouts fallback to be free plan (#3136)
* fix(limits): updated rate limiter to match execution timeouts, adjusted timeouts fallback to be free plan

* upgrade turborepo
2026-02-04 15:31:53 -08:00
Waleed
8d846c5983 feat(async-jobs): async execution with job queue backends (#3134)
* feat(async-jobs): async execution with job queue backends

* added migration

* remove unused envvar, remove extraneous comments

* ack comment

* same for db

* added dedicated async envvars for timeouts, updated helm

* updated comment

* ack comment

* migrated routes to be more restful

* ack comments

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 14:52:33 -08:00
Vikhyath Mondreti
362f4c2918 improvement(timeouts): sync to 50 min, self-hosted maxed out (#3133)
* improvement(timeouts): sync to 50 min, self-hosted maxed out

* update env vars
2026-02-04 11:27:41 -08:00
Waleed
c77e351067 fix(providers): correct tool calling message format across all providers (#3132)
* fix(providers): correct tool calling message format across all providers

* fix(bedrock): correct timestamp char count in comment

* chore(gemini): remove dead executeToolCall function

* remove unused var
2026-02-04 11:02:49 -08:00
Vikhyath Mondreti
a627faabe7 feat(timeouts): execution timeout limits (#3120)
* feat(timeouts): execution timeout limits

* fix type issues

* add to docs

* update stale exec cleanup route

* update more callsites

* update tests

* address bugbot comments

* remove import expression

* support streaming and async paths'

* fix streaming path

* add hitl and workflow handler

* make sync path match

* consolidate

* timeout errors

* validation errors typed

* import order

* Merge staging into feat/timeout-lims

Resolved conflicts:
- stt/route.ts: Keep both execution timeout and security imports
- textract/parse/route.ts: Keep both execution timeout and validation imports
- use-workflow-execution.ts: Keep cancellation console entry from feature branch
- input-validation.ts: Remove server functions (moved to .server.ts in staging)
- tools/index.ts: Keep execution timeout, use .server import for security

* make run from block consistent

* revert console update change

* fix subflow errors

* clean up base 64 cache correctly

* update docs

* consolidate workflow execution and run from block hook code

* remove unused constant

* fix cleanup base64 sse

* fix run from block tracespan
2026-02-04 10:26:36 -08:00
Vikhyath Mondreti
f811594875 improvement(rooms): redis client closed should fail with indicator (#3115)
* improvement(rooms): redis client closed should fail fast

* bugbot comment

* consolidate
2026-02-03 23:48:46 -08:00
Emir Karabeg
0bc245b7a9 feat(note-block): note block preview newlines (#3127)
* feat(note-block): add single newline support in preview

Add remark-breaks plugin to the note block markdown renderer to convert
single newlines into line breaks. This fixes the issue where users had
to use double newlines (\n\n) to create visible line breaks in the
note block preview.

Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>

* Revert "feat(note-block): add single newline support in preview"

This reverts commit 049b42502a.

* feat(note-block): add single newline support in preview

Add a preprocessor function that converts single newlines to markdown
hard breaks (two trailing spaces + newline) before rendering. This
ensures that when users press Enter in the note block editor, the
line break shows up in the preview.

The function preserves:
- Double newlines (paragraph breaks)
- Code block formatting (fenced and inline)

Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>

* refactor(note-block): simplify comments

Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>

* added remark-breaks to allow single new line

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Emir Karabeg <emir-karabeg@users.noreply.github.com>
Co-authored-by: waleed <walif6@gmail.com>
2026-02-03 20:30:39 -08:00
Waleed
0a08ac03b9 fix(import): preserve workflow colors during import (#3130)
* fix(import): preserve workflow colors during import

* cleanup
2026-02-03 20:18:08 -08:00
Waleed
7977ac88ca fix(editor): block rename applies to correct block when selection changes (#3129) 2026-02-03 20:06:03 -08:00
Vikhyath Mondreti
5b0c2156e0 improvement(files): pass user file objects around consistently (#3119)
* improvement(collab): do not refetch active workflow id

* progress on files

* more integrations

* separate server and client logic

* consolidate more code

* fix integrations

* fix types

* consolidate more code

* fix tests

* fix more bugbot comments

* fix type check

* fix circular impport

* address more bugbot comments

* fix ocr integrations

* fix typing

* remove leftover type

* address bugbot comment

* fix file block adv mode

* fix

* normalize file input

* fix v2 blocmks for ocr

* fix for v2 versions

* fix more v2 blocks

* update single file blocks

* make interface simpler

* cleanup fireflies

* remove file only annotation

* accept all types

* added wand to ssh block

* user files should be passed through

* improve docs

* fix slack to include successful execs

* fix dropbox upload file

* fix sendgrid

* fix dropbox

* fix

* fix

* update skills

* fix uploaded file

---------

Co-authored-by: waleed <walif6@gmail.com>
2026-02-03 19:50:23 -08:00
Waleed
4db6e556b7 feat(canvas): added the ability to lock blocks (#3102)
* feat(canvas): added the ability to lock blocks

* unlock duplicates of locked blocks

* fix(duplicate): place duplicate outside locked container

When duplicating a block that's inside a locked loop/parallel,
the duplicate is now placed outside the container since nothing
should be added to a locked container.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(duplicate): unlock all blocks when duplicating workflow

- Server-side workflow duplication now sets locked: false for all blocks
- regenerateWorkflowStateIds also unlocks blocks for templates
- Client-side regenerateBlockIds already handled this (for paste/import)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix code block disabled state, allow unlock from editor

* fix(lock): address code review feedback

- Fix toggle enabled using first toggleable block, not first block
- Delete button now checks isParentLocked
- Lock button now has disabled state
- Editor lock icon distinguishes block vs parent lock state

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lock): prevent unlocking blocks inside locked containers

- Editor: can't unlock block if parent container is locked
- Action bar: can't unlock block if parent container is locked
- Shows "Parent container is locked" tooltip in both cases

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lock): ensure consistent behavior across all UIs

Block Menu, Editor, Action Bar now all have identical behavior:
- Enable/Disable: disabled when locked OR parent locked
- Flip Handles: disabled when locked OR parent locked
- Delete: disabled when locked OR parent locked
- Remove from Subflow: disabled when locked OR parent locked
- Lock: always available for admins
- Unlock: disabled when parent is locked (unlock parent first)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(enable): consistent behavior - can't enable if parent disabled

Same pattern as lock: must enable parent container first before
enabling children inside it.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs(quick-reference): add lock block action

Added documentation for the lock/unlock block feature (admin only).
Note: Image placeholder added, pending actual screenshot.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* remove prefix square brackets in error notif

* add lock block image

* fix(block-menu): paste should not be disabled for locked selection

Paste creates new blocks, doesn't modify selected ones. Changed from
disableEdit (includes lock state) to !userCanEdit (permission only),
matching the Duplicate action behavior.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(workflow): extract block deletion protection into shared utility

Extract duplicated block protection logic from workflow.tsx into
a reusable filterProtectedBlocks helper in utils/block-protection-utils.ts.
This ensures consistent behavior between context menu delete and
keyboard delete operations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(workflow): extend block protection utilities for edge protection

Add isEdgeProtected, filterUnprotectedEdges, and hasProtectedBlocks
utilities. Refactor workflow.tsx to use these helpers for:
- onEdgesChange edge removal filtering
- onConnect connection prevention
- onNodeDragStart drag prevention
- Keyboard edge deletion
- Block menu disableEdit calculation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lock): address review comments for lock feature

1. Store batchToggleEnabled now uses continue to skip locked blocks
   entirely, matching database operation behavior

2. Copilot add operation now checks if parent container is locked
   before adding nested nodes (defensive check for consistency)

3. Remove unused filterUnprotectedEdges function

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(copilot): add lock checks for insert and extract operations

- insert_into_subflow: Check if existing block being moved is locked
- extract_from_subflow: Check if block or parent subflow is locked

These operations now match the UI behavior where locked blocks
cannot be moved into/out of containers.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lock): prevent duplicates inside locked containers via regenerateBlockIds

1. regenerateBlockIds now checks if existing parent is locked before
   keeping the block inside it. If parent is locked, the duplicate
   is placed outside (parentId cleared) instead of creating an
   inconsistent state.

2. Remove unnecessary effectivePermissions.canAdmin and potentialParentId
   from onNodeDragStart dependency array.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lock): fix toggle locked target state and draggable check

1. BATCH_TOGGLE_LOCKED now uses first block from blocksToToggle set
   instead of blockIds[0], matching BATCH_TOGGLE_ENABLED pattern.
   Also added early exit if blocksToToggle is empty.

2. Blocks inside locked containers are now properly non-draggable.
   Changed draggable check from !block.locked to use isBlockProtected()
   which checks both block lock and parent container lock.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(copilot): check parent lock in edit and delete operations

Both edit and delete operations now check if the block's parent
container is locked, not just if the block itself is locked. This
ensures consistent behavior with the UI which uses isBlockProtected
utility that checks both direct lock and parent lock.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(socket): add server-side lock validation and admin-only permissions

1. BATCH_TOGGLE_LOCKED now requires admin role - non-admin users with
   write role can no longer bypass UI restriction via direct socket
   messages

2. BATCH_REMOVE_BLOCKS now validates lock status server-side - filters
   out protected blocks (locked or inside locked parent) before deletion

3. Remove duplicate/outdated comment in regenerateBlockIds

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(socket): update permission test for admin-only lock toggle

batch-toggle-locked is now admin-only, so write role should be denied.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(undo-redo): use consistent target state for toggle redo

The redo logic for BATCH_TOGGLE_ENABLED and BATCH_TOGGLE_LOCKED was
incorrectly computing each block's new state as !previousStates[blockId].
However, the store's batchToggleEnabled/batchToggleLocked set ALL blocks
to the SAME target state based on the first block's previous state.

Now redo computes targetState = !previousStates[firstBlockId] and applies
it to all blocks, matching the store's behavior.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(socket): add comprehensive lock validation across operations

Based on audit findings, adds lock validation to multiple operations:

1. BATCH_TOGGLE_HANDLES - now skips locked/protected blocks at:
   - Store layer (batchToggleHandles)
   - Collaborative hook (collaborativeBatchToggleBlockHandles)
   - Server socket handler

2. BATCH_ADD_BLOCKS - server now filters blocks being added to
   locked parent containers

3. BATCH_UPDATE_PARENT - server now:
   - Skips protected blocks (locked or inside locked container)
   - Prevents moving blocks into locked containers

All validations use consistent isProtected() helper that checks both
direct lock and parent container lock.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(workflow): use pre-computed lock state from contextMenuBlocks

contextMenuBlocks already has locked and isParentLocked properties
computed in use-canvas-context-menu.ts, so there's no need to look
up blocks again via hasProtectedBlocks.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lock): add lock validation to block rename operations

Defense-in-depth: although the UI disables rename for locked blocks,
the collaborative layer and server now also validate locks.

- collaborativeUpdateBlockName: checks if block is locked or inside
  locked container before attempting rename
- UPDATE_NAME server handler: checks lock status and parent lock
  before performing database update

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* added defense in depth for renaming locked blocks

* fix(socket): add server-side lock validation for edges and subblocks

Defense-in-depth: adds lock checks to server-side handlers that were
previously relying only on client-side validation.

Edge operations (ADD, REMOVE, BATCH_ADD, BATCH_REMOVE):
- Check if source or target blocks are protected before modifying edges

Subblock updates:
- Check if parent block is protected before updating subblock values

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lock): fetch parent blocks for edge protection checks and consistent tooltip

- Fixed edge operations to fetch parent blocks before checking lock status
  - Previously, isBlockProtected checked if parent was locked, but the parent
    wasn't in blocksById because only source/target blocks were fetched
  - Now fetches parent blocks for all four edge operations: ADD, REMOVE,
    BATCH_ADD_EDGES, BATCH_REMOVE_EDGES
- Fixed tooltip inconsistency: changed "Run previous blocks first" to
  "Run upstream blocks first" in action-bar to match workflow.tsx

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* updated tooltip text for run from block

* fix(lock): add lock check to duplicate button and clean up drag handler

- Added lock check to duplicate button in action bar to prevent
  duplicating locked blocks (consistent with other edit operations)
- Removed ineffective early return in onNodeDragStart since the
  `draggable` property on nodes already prevents dragging protected
  blocks - the early return was misleading as it couldn't actually
  stop a drag operation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lock): use disableEdit for duplicate in block menu

Changed duplicate menu item to use disableEdit (which includes lock
check) instead of !userCanEdit for consistency with action bar and
other edit operations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 19:15:04 -08:00
Waleed
4ba22527b6 improvement(tag-dropdown): removed custom styling on tag dropdown popover, fixed execution ordering in terminal and loops entries (#3126)
* improvement(tag-dropdown): removeed custom styling on tag dropdown popover, fixed execution ordering in terminal and loops entries

* ack pr comments

* handle old records
2026-02-03 18:32:40 -08:00
Waleed
c51f266ad7 fix(logs): use formatDuration utility and align file cards styling (#3125) 2026-02-03 15:26:09 -08:00
Waleed
4ca00810b2 fix(http): serialize nested objects in form-urlencoded body (#3124) 2026-02-03 10:58:01 -08:00
Waleed
710bf75bca fix(sidebar): right-click replaces selection, reset popover hover state (#3123)
* fix(sidebar): right-click replaces selection, reset popover hover state

* fix(queries): add userId to superuser query key for cache isolation
2026-02-03 10:09:03 -08:00
Waleed
f21fe2309c fix(formatting): consolidate duration formatting into shared utility (#3118)
* fix(formatting): consolidate duration formatting into shared utility

* fix(formatting): preserve original precision and rounding behavior

* fix(logs): add precision to logs list duration formatting

* fix(formatting): use parseFloat to preserve fractional milliseconds

* feat(ee): add enterprise modules (#3121)

* fix(formatting): return null for missing values, strip trailing zeros
2026-02-02 23:57:08 -08:00
Waleed
9c3fd1f7af feat(ee): add enterprise modules (#3121) 2026-02-02 23:40:18 -08:00
Waleed
a9b7d75d87 feat(editor): added docs link to editor (#3116) 2026-02-02 12:22:08 -08:00
Vikhyath Mondreti
0449804ffb improvement(billing): duplicate checks for bypasses, logger billing actor consistency, run from block (#3107)
* improvement(billing): improve against direct subscription creation bypasses

* more usage of block/unblock helpers

* address bugbot comments

* fail closed

* only run dup check for orgs
2026-02-02 10:52:08 -08:00
Vikhyath Mondreti
c286f3ed24 fix(mcp): child workflow with response block returns error (#3114) 2026-02-02 09:30:35 -08:00
Vikhyath Mondreti
b738550815 fix(cleanup-cron): stale execution cleanup integer overflow (#3113) 2026-02-02 09:03:56 -08:00
Waleed
2bb68335ee v0.5.79: longer MCP tools timeout, optimize loop/parallel regeneration, enrich.so integration 2026-01-31 21:57:56 -08:00
Waleed
c6357f7438 feat(tools): added enrich so (#3103)
* feat(tools): added enrich so

* updated docs and types
2026-01-31 21:18:41 -08:00
Waleed
b1118935f7 fix(workflow): optimize loop/parallel regeneration and prevent duplicate agent tools (#3100)
* fix(workflow): optimize loop/parallel regeneration and prevent duplicate agent tools

* refactor(workflow): remove addBlock in favor of batchAddBlocks

- Migrated undo-redo to use batchAddBlocks instead of addBlock loop
- Removed addBlock method from workflow store (now unused)
- Updated tests to use helper function wrapping batchAddBlocks
- This fixes the cursor bot comments about inconsistent parent checking
2026-01-31 17:55:32 -08:00
Waleed
3e18b4186c fix(mcp): pass timeout to SDK callTool to override 60s default (#3101) 2026-01-31 17:44:49 -08:00
Vikhyath Mondreti
e1ac201936 improvement(ratelimits, sockets): increase across all plans, reconnecting notif for sockets (#3096)
* improvement(rate-limits): increase across all plans

* improve sockets with reconnecting

* address bugbot comment

* fix typing
2026-01-31 16:48:57 -08:00
Waleed
8528fbe2d2 v0.5.78: billing fixes, mcp timeout increase, reactquery migrations, updated tool param visibilities, DSPy and Google Maps integrations 2026-01-31 13:48:22 -08:00
Waleed
6cb3977dd9 fix(visibility): updated visibility for non-sensitive tool params from user only to user or llm (#3095)
* fix(visibility): updated visibility for non-sensitive tool params from user only to user or llm

* update docs

* updated docs script
2026-01-31 11:31:08 -08:00
Waleed
e11758fb43 feat(tools): added google maps and DSPy (#3098)
* feat(tools): added google maps and DSPy

* updated docs

* updated broken import path

* updated icon
2026-01-31 11:08:35 -08:00
Vikhyath Mondreti
cf2f1abcaf fix(executor): condition inside parallel (#3094)
* fix(executor): condition inside parallel

* remove comments
2026-01-30 18:47:39 -08:00
Waleed
4109feecf6 feat(invitations): added invitations query hook, migrated all tool files to use absolute imports (#3092)
* feat(invitations): added invitations query hook, migrated all tool files to use absolute imports

* ack PR comments

* remove dead import

* remove unused hook
2026-01-30 18:39:23 -08:00
Waleed
37d5e01f5f fix(mcp): increase timeout from 1m to 10m (#3093) 2026-01-30 17:51:05 -08:00
Vikhyath Mondreti
2d799b3272 fix(billing): plan should be detected from stripe subscription object (#3090)
* fix(billing): plan should be detected from stripe subscription object

* fix typing
2026-01-30 17:01:16 -08:00
Waleed
92403e0594 fix(editor): advanced toggle respects user edit permissions (#3089) 2026-01-30 15:22:46 -08:00
Waleed
31fdd2be13 v0.5.77: room manager redis migration, tool outputs, ui fixes 2026-01-30 14:57:17 -08:00
Emir Karabeg
2c4eb9fecb fix(terminal): start precision (#3078)
* fix(executor): use performance.now() for precise block timing

Replace Date.now() with performance.now() for timing measurements in
the executor to provide sub-millisecond precision. This fixes timing
discrepancies with fast-executing blocks like the start block where
millisecond precision was insufficient.

Changes:
- block-executor.ts: Use performance.now() for block execution timing
- engine.ts: Use performance.now() for overall execution timing

Co-authored-by: emir <emir@simstudio.ai>

* format ms as whole nums,round secs to 2 decimal places and compute all started/ended times on server and passback to clinet

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: waleed <walif6@gmail.com>
2026-01-30 12:52:08 -08:00
Emir Karabeg
aec0de046b feat(note-block): enable body dragging to match workflow block (#3073)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-01-30 12:51:40 -08:00
Emir Karabeg
51565a6e28 fix(note): remove icon from note block in preview (#3075)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-01-30 12:46:20 -08:00
Vikhyath Mondreti
a280a53034 fix(agent-logs): don't filter out agent cost from trace span (#3086) 2026-01-30 12:31:31 -08:00
Vikhyath Mondreti
478a53521e improvement(docker): update docker-compose env vars (#3080)
* improvement(docker): add internal api secret to docker compose

* remove dead code

* remove more dead code

* add api encryption key to this too

* update
2026-01-30 12:14:40 -08:00
Waleed
6cf9841b99 fix(invite-modal): remove custom button heights and useEffect anti-pattern (#3082)
* fix(invite-modal): remove custom button heights and useEffect anti-pattern

* cleanup
2026-01-30 12:14:27 -08:00
Vikhyath Mondreti
656beb8383 fix(anthropic): token limits for streaming with tool calls (#3084)
* remove for bedrock since they handle on their own

* fix

* fix inference config reference

* add to docs

* make it min between max tokens
2026-01-30 12:08:16 -08:00
Waleed
f7c3de0591 fix(streaming): handle multi-byte UTF-8 chars split across chunks (#3083) 2026-01-30 11:39:52 -08:00
Waleed
2ec9b7f47e feat(deployments): human-readable version descriptions (#3077) 2026-01-30 11:17:18 -08:00
Vikhyath Mondreti
b0fbf3648d improvment(sockets): migrate to redis (#3072)
* improvment(sockets): migrate to redis

* remove random error code

* improve typing

* use native api

* fix bugbot comments

* bugbot comment

* fix more bugbot cleanup comments

* null cursor

* fix

* cleanup code

* fix bugbot comments
2026-01-30 09:47:15 -08:00
Waleed
f718079593 improvement(docs): instant copy button + performance optimizations (#3076) 2026-01-29 23:24:58 -08:00
Waleed
dd2f0c6a6a feat(tools): added similarweb (#3071) 2026-01-29 20:39:23 -08:00
Waleed
f99518b837 feat(calcom): added calcom (#3070)
* feat(tools): added calcom

* added more triggers, tested

* updated regex in script for release to be more lenient

* fix(tag-dropdown): performance improvements and scroll bug fixes

- Add flatTagIndexMap for O(1) tag lookups (replaces O(n²) findIndex calls)
- Memoize caret position calculation to avoid DOM manipulation on every render
- Use refs for inputValue/cursorPosition to keep handleTagSelect callback stable
- Change itemRefs from index-based to tag-based keys to prevent stale refs
- Fix scroll jump in nested folders by removing scroll reset from registerFolder
- Add onFolderEnter callback for scroll reset when entering folder via keyboard
- Disable keyboard navigation wrap-around at boundaries
- Simplify selection reset to single effect on flatTagList.length change

Also:
- Add safeCompare utility for timing-safe string comparison
- Refactor webhook signature validation to use safeCompare

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* updated types

* fix(calcom): simplify required field constraints for booking attendee

The condition field already restricts these to calcom_create_booking,
so simplified to required: true. Per Cal.com API docs, email is optional
while name and timeZone are required.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* added tests

* updated folder multi select, updated calcom and github tools and docs generator script

* updated drag, updated outputs for tools, regen docs with nested docs script

* updated setup instructions links, destructure trigger outputs, fix text subblock styling

* updated docs gen script

* updated docs script

* updated docs script

* updated script

* remove destructuring of stripe webhook

* expanded wand textarea, updated calcom tools

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 20:37:30 -08:00
Siddharth Ganesan
2b026ded16 fix(copilot): hosted api key validation + credential validation (#3000)
* Fix

* Fix greptile

* Fix validation

* Fix comments

* Lint

* Fix

* remove passed in workspace id ref

* Fix comments

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-29 10:48:59 -08:00
Siddharth Ganesan
dca0758054 fix(executor): conditional deactivation for loops/parallels (#3069)
* Fix deactivation

* Remove comments
2026-01-29 10:43:30 -08:00
Waleed
028bc652c2 v0.5.76: posthog improvements, readme updates 2026-01-29 00:13:19 -08:00
Waleed
ae17c90bdf chore(readme): update readme.md (#3066) 2026-01-28 23:51:34 -08:00
Waleed
1256a15266 fix(posthog): move session recording proxy to middleware for large payload support (#3065)
Next.js rewrites can strip request bodies for large payloads (1MB+),
causing 400 errors from CloudFront. PostHog session recordings require
up to 64MB per message. Moving the proxy to middleware ensures proper
body passthrough.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 23:49:57 -08:00
Waleed
0b2b7ed9c8 fix(oauth): use createElement for icon components to fix React hooks error (#3064) 2026-01-28 23:37:00 -08:00
Waleed
c6bf5cd58c v0.5.75: search modal overhaul, helm chart updates, run from block, terminal and visual debugging improvements 2026-01-28 22:54:13 -08:00
Vikhyath Mondreti
0d8d9fb238 fix(type): logs workspace delivery (#3063) 2026-01-28 21:54:20 -08:00
Vikhyath Mondreti
e0f1e66f4f feat(child-workflows): nested execution snapshots (#3059)
* feat(child-workflows): nested execution snapshots

* cleanup typing

* address bugbot comments and fix tests

* do not cascade delete logs/snapshots

* fix few more inconsitencies

* fix external logs route

* add fallback color
2026-01-28 19:40:52 -08:00
Emir Karabeg
20bb7cdec6 improvement(preview): include current workflow badge in breadcrumb in workflow snapshot (#3062)
* feat(preview): add workflow context badge for nested navigation

Adds a badge next to the Back button when viewing nested workflows
to help users identify which workflow they are currently viewing.
This is especially helpful when navigating deeply into nested
workflow blocks.

Changes:
- Added workflowName field to WorkflowStackEntry interface
- Capture workflow name from metadata when drilling down
- Display workflow name badge next to Back button

Co-authored-by: emir <emir@simstudio.ai>

* added workflow name and desc to metadata for workflow preview

* added copy and search icon in code in preview editor

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: waleed <walif6@gmail.com>
2026-01-28 19:33:19 -08:00
Waleed
1469e9c66c feat(youtube): add captions, trending, and video categories tools with enhanced API coverage (#3060)
* feat(youtube): add captions, trending, and video categories tools with enhanced API coverage

* fix(youtube): remove captions tool (requires OAuth), fix tinybird defaults, encode pageToken
2026-01-28 19:08:33 -08:00
Waleed
06d7ce7667 feat(timeout): add API block timeout configuration (#3053)
* feat(timeout): add timeout subblock to the api block

* fix(timeout): honor timeout config for internal routes and fix type coercion

- Add AbortController support for internal routes (/api/*) to honor timeout
- Fix type coercion: convert string timeout from short-input to number
- Handle NaN gracefully by falling back to undefined (default timeout)

Fixes #2786
Fixes #2242

* fix: remove redundant clearTimeout in catch block

* fix: validate timeout is positive number

Negative timeout values would cause immediate request abort since
JavaScript treats negative setTimeout delays as 0.

* update docs image, update search modal performance

* removed unused keywords type

* ack comments

* cleanup

* fix: add default timeout for internal routes and validate finite timeout

- Internal routes now use same 5-minute default as external routes
- Added Number.isFinite() check to reject Infinity values

* fix: enforce max timeout and improve error message consistency

- Clamp timeout to max 600000ms (10 minutes) as documented
- External routes now report timeout value in error message

* remove unused code
2026-01-28 17:14:26 -08:00
Emir Karabeg
1bc476f10b fix(copilot): panning on workflow (#3057) 2026-01-28 16:37:12 -08:00
Vikhyath Mondreti
9e40342af8 fix(snapshot): consolidate to use hasWorkflowChanges check (#3051)
* fix(snapshot): consolidate to use hasWorkflowChanges check

* Remove debug logs

* fix normalization logic

* fix serializer for canonical modes
2026-01-28 16:29:17 -08:00
Waleed
0c0f19c717 fix(icons): update strokeWidth of action bar items to match, update run from block icon to match run workflow button (#3056)
* fix(icons): update strokeWidth of action bar items to match, update run from block icon to match run workflow button

* update docs
2026-01-28 16:24:06 -08:00
Emir Karabeg
12d529d045 fix: terminal spacing, subflow disabled in preview (#3055)
* fix: terminal spacing, subflow disabled in preview

* addressed comments
2026-01-28 15:41:46 -08:00
Vikhyath Mondreti
57f0837da7 fix(child-workflow-error-spans): pass trace-spans accurately in block logs (#3054)
* fix(child-workflow): must bypass hiddenFromDisplay config

* fix passing of spans to be in block log

* keep fallback for backwards compat

* fix error message formatting

* clean up
2026-01-28 14:54:35 -08:00
Emir Karabeg
5c02d46d55 feat(terminal): structured output (#3026)
* feat(code): collapsed JSON in terminal

* improvement(code): addressed comments

* feat(terminal): added structured output; improvement(preview): note block

* feat(terminal): log view

* improvement(terminal): ui/ux

* improvement(terminal): default sizing and collapsed width

* fix: code colors, terminal large output handling

* fix(terminal): structured search

* improvement: preivew accuracy, invite-modal admin, logs live
2026-01-28 14:40:43 -08:00
Waleed
8b2404752b feat(description): add deployment version descriptions (#3048)
* feat(description): added version description for deployments table

* feat(description): refactor to tanstack query and remove useEffect

* add wand to generate diff

* ack comments

* removed redundant logic, kept single source of truth for diff

* updated docs

* use consolidated sse parsing util, add loops & parallels check

* DRY
2026-01-28 13:52:40 -08:00
Waleed
c00f05c346 fix(tests): use UTC methods for timezone-independent schedule assertions (#3052) 2026-01-28 13:50:22 -08:00
Vikhyath Mondreti
78410eef84 improvement(inputs): sanitize trigger inputs better (#3047) 2026-01-28 12:57:20 -08:00
Siddharth Ganesan
655fe4f3b7 feat(executor): run from/until block (#3029)
* Run from block

* Fixes

* Fix

* Fix

* Minor improvements

* Fix

* Fix trace spans

* Fix loop l ogs

* Change ordering

* Run u ntil block

* Lint

* Clean up

* Fix

* Allow run from block for triggers

* Consolidation

* Fix lint

* Fix

* Fix mock payload

* Fix

* Fix trigger clear snapshot

* Fix loops and parallels

* Fix

* Cleanup

* Fix test

* Fix bugs

* Catch error

* Fix

* Fix

* I think it works??

* Fix

* Fix

* Add tests

* Fix lint

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-28 12:53:23 -08:00
Waleed
72a2f79701 improvement(search-modal): add quick navigation items and fix cmdk value uniqueness (#3050)
* improvement(search-modal): add quick navigation items and fix cmdk value uniqueness

* rerank
2026-01-28 12:39:00 -08:00
Waleed
2c2b485f81 fix(workflow): update container dimensions on keyboard movement (#3043)
* fix(workflow): update container dimensions on keyboard movement

* fix(workflow): avoid duplicate container updates during drag

Add !change.dragging check to only handle keyboard movements in
onNodesChange, since mouse drags are already handled by onNodeDrag.

* fix(workflow): persist keyboard movements to backend

Keyboard arrow key movements now call collaborativeBatchUpdatePositions
to sync position changes to the backend for persistence and real-time
collaboration.

* improvement(cmdk): refactor search modal to use cmdk + fix icon SVG IDs (#3044)

* improvement(cmdk): refactor search modal to use cmdk + fix icon SVG IDs

* chore: remove unrelated workflow.tsx changes

* chore: remove comments

* chore: add devtools middleware to search modal store

* fix: allow search data re-initialization when permissions change

* fix: include keywords in search filter + show service name in tool operations

* fix: correct filterBlocks type signature

* fix: move generic to function parameter position

* fix(mcp): correct event handler type for onInput

* perf: always render command palette for instant opening

* fix: clear search input when modal reopens

* fix(helm): move rotationPolicy under privateKey for cert-manager compatibility (#3046)

* fix(helm): move rotationPolicy under privateKey for cert-manager compatibility

* docs(helm): add reclaimPolicy Retain guidance for production database storage

* fix(helm): prevent empty branding ConfigMap creation

* fix(workflow): avoid duplicate position updates on drag end

Check isInDragOperation before persisting in onNodesChange to prevent
duplicate calls. Drag-end events have dragStartPosition still set,
while keyboard movements don't, allowing proper distinction.
2026-01-28 12:31:38 -08:00
Siddharth Ganesan
01e0723a3a fix(loops): fix loops on empty collection (#3049)
* Fix

* Cleanup

* order of ops for validations

* only reachable subflow nodes should hit validation

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-28 12:16:36 -08:00
Waleed
6814f33243 fix(helm): move rotationPolicy under privateKey for cert-manager compatibility (#3046)
* fix(helm): move rotationPolicy under privateKey for cert-manager compatibility

* docs(helm): add reclaimPolicy Retain guidance for production database storage

* fix(helm): prevent empty branding ConfigMap creation
2026-01-28 10:51:19 -08:00
Waleed
304cf717a4 improvement(cmdk): refactor search modal to use cmdk + fix icon SVG IDs (#3044)
* improvement(cmdk): refactor search modal to use cmdk + fix icon SVG IDs

* chore: remove unrelated workflow.tsx changes

* chore: remove comments

* chore: add devtools middleware to search modal store

* fix: allow search data re-initialization when permissions change

* fix: include keywords in search filter + show service name in tool operations

* fix: correct filterBlocks type signature

* fix: move generic to function parameter position

* fix(mcp): correct event handler type for onInput

* perf: always render command palette for instant opening

* fix: clear search input when modal reopens
2026-01-28 10:38:09 -08:00
Vikhyath Mondreti
11dc18a80d v0.5.74: autolayout improvements, clerk integration, auth enforcements 2026-01-27 20:37:39 -08:00
Vikhyath Mondreti
0d0209a108 fix(autolayout): pass through gridsize (#3042)
* fix(autolayout): pass through gridsize

* fix tests
2026-01-27 20:09:26 -08:00
Vikhyath Mondreti
500dcd4734 fix(openrouter): ignored when tools are configured but unused (#3041) 2026-01-27 19:43:44 -08:00
Vikhyath Mondreti
8bdba373c6 improvement(function): timeout increase to 5 min (#3040) 2026-01-27 19:32:11 -08:00
Vikhyath Mondreti
c8ffda1616 fix(gemini): token count (#3039)
* fix(gemini): token count

* fix to include tool call tokens
2026-01-27 19:16:54 -08:00
Waleed
b4a389a71f improvement(helm): update GPU device plugin and add cert-manager issuers (#3036)
* improvement(helm): update GPU device plugin and add cert-manager issuers

* fix(helm): address code review feedback for GPU plugin and cert-manager

* fix(helm): remove duplicate nodeSelector, add hook for CA issuer ordering

* fix(helm): remove incorrect hook, CA issuer auto-reconciles
2026-01-27 18:25:08 -08:00
Vikhyath Mondreti
65bc21608c improvement(block-inputs): must parse json accurately + models max_tokens fix (#3033)
* improvement(block-inputs): must parse json accurately

* fix sheets typing

* add reference comment

* fix models

* revert blocks changes

* fix param to follow openai new convention
2026-01-27 18:17:35 -08:00
Waleed
ef613ef035 fix(models): update cerebras and groq models (#3038) 2026-01-27 18:12:48 -08:00
Waleed
20b76e67b3 improvement(skills): extend skills (#3035) 2026-01-27 17:58:58 -08:00
Waleed
7640fdf742 feat(autolayout): add snap-to-grid support (#3031)
* feat(autolayout): add snap-to-grid support

* fix(autolayout): recalculate dimensions after grid snapping

* fix(autolayout): correct dimension calculation and propagate gridSize
2026-01-27 17:02:27 -08:00
Waleed
bca355c36d feat(tools): added clerk tools and block (#3032)
* feat(tools): added clerk tools and block

* updated docs gen script

* use clerk api types
2026-01-27 16:45:48 -08:00
Vikhyath Mondreti
089427822e fix(hitl): add missing fields to block configs (#3027)
* fix(hitl): add missing fields to block configs

* update copilot paths

* one more case

* update helper usage
2026-01-27 14:35:37 -08:00
Waleed
6b412c578d fix(security): add authentication to remaining tool API routes (#3028)
* fix(security): add authentication to tool API routes

* fix(drive): use checkSessionOrInternalAuth to allow browser access

* fix(selectors): use checkSessionOrInternalAuth for UI-accessible routes
2026-01-27 12:37:03 -08:00
Waleed
dddd0c8277 fix(workflow): use panel-aware viewport center for paste and block placement (#3024) 2026-01-27 12:36:38 -08:00
Waleed
be7f3db059 fix(badge): add type variant for dark mode contrast (#3025)
* fix(badge): add type variant for dark mode contrast

* docs(badge): add type variant to TSDoc
2026-01-27 11:40:14 -08:00
Waleed
416c08267a fix(terminal): persist collapsed state across page refresh (#3023)
* fix(terminal): persist collapsed state across page refresh

* fix(terminal): add activeWorkflowId to auto-open effect deps
2026-01-27 11:38:44 -08:00
Waleed
ab4e9dc72f v0.5.73: ci, helm updates, kb, ui fixes, note block enhancements 2026-01-26 22:04:35 -08:00
Waleed
46ba315701 feat(tools): added more intercom tools (#3022) 2026-01-26 21:41:44 -08:00
Waleed
077e702dd8 improvement(tools): updated kalshi and polymarket tools and blocks (#3021) 2026-01-26 21:01:33 -08:00
Waleed
d8df08d3d3 improvement(mcp): remove mcp-remote for cursor config (#3020) 2026-01-26 19:54:27 -08:00
Vikhyath Mondreti
51891daf9a feat(code): undo-redo state (#3018)
* feat(code): undo-redo state

* address greptile

* address bugbot comments

* fix debounce flush

* inc debounce time

* fix wand case

* address comments
2026-01-26 19:40:40 -08:00
Vikhyath Mondreti
9ee5dfe185 improvement(workflow): hide raw json childworkflow span (#3019) 2026-01-26 18:47:35 -08:00
Emir Karabeg
9cba8eee48 improvement(preview): error paths, loops, workflow (#3010)
* improvement(switch): dark styling

* improvement(settings): change deployed MCPs to MCPs servers

* improvement(preview): added error paths, loop logic

* improvement(preview): nested workflows preview

* feat(preview): lightweight param

* improvement(preview): staging changes integrated
2026-01-26 17:57:48 -08:00
Siddharth Ganesan
cb650132c7 fix(executor): fix. convergent error edges (#3015) 2026-01-26 17:25:09 -08:00
Waleed
9dbf56f9cd feat(note-block): expand media embed support with tuned aspect ratios (#3016)
* feat(note-block): expand media embed support with tuned aspect ratios

* fix(note-block): add artist parameter to Bandcamp embed URLs

Include the artist subdomain in Bandcamp track and album embed URLs
to ensure proper embed resolution.

* fix(note-block): add required src attribute to track elements

HTML spec requires track elements to have a src attribute.

* fix(note-block): address embed URL matching issues

- Fix YouTube regex to handle v= anywhere in query params
- Fix Twitch channel match to exclude /clip/ URLs
- Remove Mux support (HLS not supported in most browsers)
- Remove Bandcamp support (requires numeric IDs, not slugs)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 17:22:44 -08:00
Vikhyath Mondreti
5189473e06 fix(max-tokens): anthropic models streaming vs non-streaming (#2999)
* fix(max-tokens): anthropic models streaming vs non-streaming

* default max inc

* update the docs
2026-01-26 17:05:33 -08:00
Vikhyath Mondreti
37900988eb fix(kb): workspace id required for creation (#3001)
* fix(kb): workspace id required for creation

* fix tests
2026-01-26 16:27:07 -08:00
Vikhyath Mondreti
3cc9b1ae56 fix(input-format): resolution for blocks with input format fields (#3012)
* fix input format

* fix tests

* address bugbot comment
2026-01-26 16:04:19 -08:00
Waleed
3ccbee187d improvement(docs): updated logo, added lightbox to action media, fixed minor styling inconsistencies between themes (#3014)
* improvement(docs): updated logo, added lightbox to action media, fixed minor styling inconsistencies between themes

* updated og image

* ack comments
2026-01-26 15:49:23 -08:00
Waleed
36945deaa5 improvement(preview): consolidate block rendering and fix handle configurations (#3013)
* improvement(preview): consolidate block rendering and fix handle configurations

* refactor(preview): extract SubflowContainerProps interface
2026-01-26 14:56:06 -08:00
Waleed
ebf2852733 fix(copilot): reliable zoom to changed blocks after diff applied (#3011) 2026-01-26 13:54:01 -08:00
Waleed
12495ef89c feat(ci): auto-create github releases and add workflow permissions (#3009) 2026-01-26 13:28:59 -08:00
Waleed
d8d85fccf0 feat(helm): add branding configmap for custom assets (#3008) 2026-01-26 13:19:23 -08:00
Waleed
56bc809c6f fix(docs): separate local and blob asset resolution for quick-reference (#3007)
* fix(docs): separate local and blob asset resolution for quick-reference

ActionImage now uses local paths directly for PNGs while ActionVideo
uses blob storage with proper path normalization (strips static/ prefix).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(docs): simplify asset resolution by using correct paths directly

Remove path normalization logic from action-media component. Instead,
use the appropriate paths in MDX:
- PNGs: /static/quick-reference/... (local)
- MP4s: quick-reference/... (blob via getAssetUrl)

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 13:07:11 -08:00
Vikhyath Mondreti
c7bd48573a fix(codegen): function prologue resolution edge cases (#3005)
* fix(codegen): function prologue resolution edge cases

* remove hacky fallback

* case insensitive lookup

* fix python nan and inf resolution

* remove template literal check

* fix tests

* consolidate literal gen
2026-01-26 10:16:13 -08:00
Vikhyath Mondreti
1c58c35bd8 v0.5.72: azure connection string, supabase improvement, multitrigger resolution, docs quick reference 2026-01-25 23:42:27 -08:00
Waleed
80f00479a3 improvement(docs): added images and videos to quick references (#3004)
* improvement(docs): added images and videos to quick references

* moved mp4s to blob, completed quick reference guide
2026-01-25 23:31:40 -08:00
Vikhyath Mondreti
c140e90559 fix(multi-trigger): resolution paths for triggers (#3002)
* fix(multi-trigger): resolution paths for triggers

* fix trigger input format version

* fix output condition logic

* update type guard:

* fix
2026-01-25 23:20:42 -08:00
Vikhyath Mondreti
d83c418111 fix(supabase): storage upload + add basic mode version (#2996)
* fix(supabase): storage upload + add basic mode version

* fix subblock update

* remove redundant check in a2a

* add check consistently for baseline diff
2026-01-25 14:19:30 -08:00
Waleed
be2a9ef0f8 fix(storage): support Azure connection string for presigned URLs (#2997)
* fix(docs): update requirements to be more accurate for deploying the app

* updated kb to support 1536 dimension vectors for models other than text embedding 3 small

* fix(storage): support Azure connection string for presigned URLs

* fix(kb): update test for embedding dimensions parameter

* fix(storage): align credential source ordering for consistency
2026-01-25 13:06:12 -08:00
Waleed
d63a5cb504 v0.5.71: ux, ci improvements, docs updates 2026-01-25 03:08:08 -08:00
Waleed
1bf5ed4586 improvement(docs): add quick reference page and update SDK documentation (#2994)
* docs(sdk): update README to reflect new interface

* improvement(docs): add quick reference page and update SDK documentation

* docs(copilot): update copilot documentation with all features
2026-01-25 02:21:02 -08:00
Waleed
dc0ed842c4 fix(sdk): improve input handling and separate input from options (#2993)
* fix(sdk): improve input handling and separate input from options

* fix(sdk): treat null as no input for consistency with Python SDK
2026-01-25 00:50:09 -08:00
Waleed
1952b196a0 fix(releases): improve commit categorization and ci security (#2992)
* fix(releases): improve commit categorization and CI security

* fix(releases): remove redundant update check
2026-01-24 22:33:04 -08:00
Vikhyath Mondreti
fa03d4d818 fix(copilot): canonical modes should be constructed on edit (#2989)
* fix(copilot): canonical modes should be constructed

* reuse canonicalIndex

* fix insert into subflow case:
2026-01-24 22:13:57 -08:00
Waleed
e14cebeec5 fix(context-menu): preserve selection when right-clicking selected block (#2991)
* fix(context-menu): preserve selection when right-clicking selected block

* added tsdoc
2026-01-24 22:05:27 -08:00
Waleed
404d8c006e fix(tooltip): add tooltip to canonical toggle button (#2990)
* fix(tooltip): add tooltip to canonical toggle button

* ack comments
2026-01-24 21:58:09 -08:00
Waleed
8bd5d41723 v0.5.70: router fix, anthropic agent response format adherence 2026-01-24 20:57:02 -08:00
Waleed
ac91d78834 fix(anthropic): use anthropic sdk to transform malformed response schemas to anthropic format (#2988)
* fix(anthropic): use anthropic sdk to transform malformed response schemas to anthropic format

* copy internal transformJSONSchema from anthropic

* remove dep update

* use built-in func from anthropic
2026-01-24 20:31:07 -08:00
Waleed
6f0a093869 fix(llm): update router and llm_chat tool to call providers routes (#2986)
* fix(llm): update router and llm_chat tool to call providers routes

* updated failing tests
2026-01-24 18:50:37 -08:00
Waleed
c12931bc50 v0.5.69: kb upgrades, blog, copilot improvements, auth consolidation (#2973)
* fix(subflows): tag dropdown + resolution logic (#2949)

* fix(subflows): tag dropdown + resolution logic

* fixes;

* revert parallel change

* chore(deps): bump posthog-js to 1.334.1 (#2948)

* fix(idempotency): add conflict target to atomicallyClaimDb query + remove redundant db namespace tracking (#2950)

* fix(idempotency): add conflict target to atomicallyClaimDb query

* delete needs to account for namespace

* simplify namespace filtering logic

* fix cleanup

* consistent target

* improvement(kb): add document filtering, select all, and React Query migration (#2951)

* improvement(kb): add document filtering, select all, and React Query migration

* test(kb): update tests for enabledFilter and removed userId params

* fix(kb): remove non-null assertion, add explicit guard

* improvement(logs): trace span, details (#2952)

* improvement(action-bar): ordering

* improvement(logs): details, trace span

* feat(blog): v0.5 release post (#2953)

* feat(blog): v0.5 post

* improvement(blog): simplify title and remove code block header

- Simplified blog title from Introducing Sim Studio v0.5 to Introducing Sim v0.5
- Removed language label header and copy button from code blocks for cleaner appearance

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* ack PR comments

* small styling improvements

* created system to create post-specific components

* updated componnet

* cache invalidation

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(admin): add credits endpoint to issue credits to users (#2954)

* feat(admin): add credits endpoint to issue credits to users

* fix(admin): use existing credit functions and handle enterprise seats

* fix(admin): reject NaN and Infinity in amount validation

* styling

* fix(admin): validate userId and email are strings

* improvement(copilot): fast mode, subagent tool responses and allow preferences (#2955)

* Improvements

* Fix actions mapping

* Remove console logs

* fix(billing): handle missing userStats and prevent crashes (#2956)

* fix(billing): handle missing userStats and prevent crashes

* fix(billing): correct import path for getFilledPillColor

* fix(billing): add Number.isFinite check to lastPeriodCost

* fix(logs): refresh logic to refresh logs details (#2958)

* fix(security): add authentication and input validation to API routes (#2959)

* fix(security): add authentication and input validation to API routes

* moved utils

* remove extraneous commetns

* removed unused dep

* improvement(helm): add internal ingress support and same-host path consolidation (#2960)

* improvement(helm): add internal ingress support and same-host path consolidation

* improvement(helm): clean up ingress template comments

Simplify verbose inline Helm comments and section dividers to match the
minimal style used in services.yaml.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(helm): add missing copilot path consolidation for realtime host

When copilot.host equals realtime.host but differs from app.host,
copilot paths were not being routed. Added logic to consolidate
copilot paths into the realtime rule for this scenario.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* improvement(helm): follow ingress best practices

- Remove orphan comments that appeared when services were disabled
- Add documentation about path ordering requirements
- Paths rendered in order: realtime, copilot, app (specific before catch-all)
- Clean template output matching industry Helm chart standards

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(blog): enterprise post (#2961)

* feat(blog): enterprise post

* added more images, styling

* more content

* updated v0-5 post

* remove unused transition

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>

* fix(envvars): resolution standardized (#2957)

* fix(envvars): resolution standardized

* remove comments

* address bugbot

* fix highlighting for env vars

* remove comments

* address greptile

* address bugbot

* fix(copilot): mask credentials fix (#2963)

* Fix copilot masking

* Clean up

* Lint

* improvement(webhooks): remove dead code (#2965)

* fix(webhooks): subscription recreation path

* improvement(webhooks): remove dead code

* fix tests

* address bugbot comments

* fix restoration edge case

* fix more edge cases

* address bugbot comments

* fix gmail polling

* add warnings for UI indication for credential sets

* fix(preview): subblock values (#2969)

* fix(child-workflow): nested spans handoff (#2966)

* fix(child-workflow): nested spans handoff

* remove overly defensive programming

* update type check

* type more code

* remove more dead code

* address bugbot comments

* fix(security): restrict API key access on internal-only routes (#2964)

* fix(security): restrict API key access on internal-only routes

* test(security): update function execute tests for checkInternalAuth

* updated agent handler

* move session check higher in checkSessionOrInternalAuth

* extracted duplicate code into helper for resolving user from jwt

* fix(copilot): update copilot chat title (#2968)

* fix(hitl): fix condition blocks after hitl (#2967)

* fix(notes): ghost edges (#2970)

* fix(notes): ghost edges

* fix deployed state fallback

* fallback

* remove UI level checks

* annotation missing from autoconnect source check

* improvement(docs): loop and parallel var reference syntax (#2975)

* fix(blog): slash actions description (#2976)

* improvement(docs): loop and parallel var reference syntax

* fix(blog): slash actions description

* fix(auth): copilot routes (#2977)

* Fix copilot auth

* Fix

* Fix

* Fix

* fix(copilot): fix edit summary for loops/parallels (#2978)

* fix(integrations): hide from tool bar (#2544)

* fix(landing): ui (#2979)

* fix(edge-validation): race condition on collaborative add (#2980)

* fix(variables): boolean type support and input improvements (#2981)

* fix(variables): boolean type support and input improvements

* fix formatting

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Emir Karabeg <78010029+emir-karabeg@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-24 14:29:53 -08:00
Waleed
bcf6dc8828 fix(variables): boolean type support and input improvements (#2981)
* fix(variables): boolean type support and input improvements

* fix formatting
2026-01-24 13:52:09 -08:00
Vikhyath Mondreti
841cb638fb fix(edge-validation): race condition on collaborative add (#2980) 2026-01-24 13:19:52 -08:00
Emir Karabeg
c7db48e3a2 fix(landing): ui (#2979) 2026-01-24 13:04:06 -08:00
Siddharth Ganesan
4d844651c2 fix(integrations): hide from tool bar (#2544) 2026-01-24 12:45:14 -08:00
Siddharth Ganesan
9f916940b3 fix(copilot): fix edit summary for loops/parallels (#2978) 2026-01-24 12:36:43 -08:00
Siddharth Ganesan
3bbf7f5d1d fix(auth): copilot routes (#2977)
* Fix copilot auth

* Fix

* Fix

* Fix
2026-01-24 12:26:21 -08:00
Vikhyath Mondreti
68683258c3 fix(blog): slash actions description (#2976)
* improvement(docs): loop and parallel var reference syntax

* fix(blog): slash actions description
2026-01-24 11:46:07 -08:00
Vikhyath Mondreti
fc7f56e21b improvement(docs): loop and parallel var reference syntax (#2975) 2026-01-24 11:36:47 -08:00
Vikhyath Mondreti
8429040921 fix(notes): ghost edges (#2970)
* fix(notes): ghost edges

* fix deployed state fallback

* fallback

* remove UI level checks

* annotation missing from autoconnect source check
2026-01-24 11:20:28 -08:00
Siddharth Ganesan
8574e6c71f fix(hitl): fix condition blocks after hitl (#2967) 2026-01-24 10:19:19 -08:00
Siddharth Ganesan
9c3e663cd8 fix(copilot): update copilot chat title (#2968) 2026-01-24 10:18:13 -08:00
Waleed
48adaa00d8 fix(security): restrict API key access on internal-only routes (#2964)
* fix(security): restrict API key access on internal-only routes

* test(security): update function execute tests for checkInternalAuth

* updated agent handler

* move session check higher in checkSessionOrInternalAuth

* extracted duplicate code into helper for resolving user from jwt
2026-01-24 10:15:52 -08:00
Vikhyath Mondreti
211a7ac3a4 fix(child-workflow): nested spans handoff (#2966)
* fix(child-workflow): nested spans handoff

* remove overly defensive programming

* update type check

* type more code

* remove more dead code

* address bugbot comments
2026-01-24 02:39:13 -08:00
Emir Karabeg
0f9b6ad1d2 fix(preview): subblock values (#2969) 2026-01-24 02:32:08 -08:00
Vikhyath Mondreti
12100e6881 improvement(webhooks): remove dead code (#2965)
* fix(webhooks): subscription recreation path

* improvement(webhooks): remove dead code

* fix tests

* address bugbot comments

* fix restoration edge case

* fix more edge cases

* address bugbot comments

* fix gmail polling

* add warnings for UI indication for credential sets
2026-01-23 23:18:20 -08:00
Siddharth Ganesan
23294683e1 fix(copilot): mask credentials fix (#2963)
* Fix copilot masking

* Clean up

* Lint
2026-01-23 19:34:55 -08:00
Vikhyath Mondreti
b913cff46e fix(envvars): resolution standardized (#2957)
* fix(envvars): resolution standardized

* remove comments

* address bugbot

* fix highlighting for env vars

* remove comments

* address greptile

* address bugbot
2026-01-23 18:59:04 -08:00
Waleed
428781ce7d feat(blog): enterprise post (#2961)
* feat(blog): enterprise post

* added more images, styling

* more content

* updated v0-5 post

* remove unused transition

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-23 18:58:00 -08:00
Waleed
f0ee67f3ed improvement(helm): add internal ingress support and same-host path consolidation (#2960)
* improvement(helm): add internal ingress support and same-host path consolidation

* improvement(helm): clean up ingress template comments

Simplify verbose inline Helm comments and section dividers to match the
minimal style used in services.yaml.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(helm): add missing copilot path consolidation for realtime host

When copilot.host equals realtime.host but differs from app.host,
copilot paths were not being routed. Added logic to consolidate
copilot paths into the realtime rule for this scenario.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* improvement(helm): follow ingress best practices

- Remove orphan comments that appeared when services were disabled
- Add documentation about path ordering requirements
- Paths rendered in order: realtime, copilot, app (specific before catch-all)
- Clean template output matching industry Helm chart standards

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 18:44:18 -08:00
Waleed
f44594c380 fix(security): add authentication and input validation to API routes (#2959)
* fix(security): add authentication and input validation to API routes

* moved utils

* remove extraneous commetns

* removed unused dep
2026-01-23 17:48:39 -08:00
Emir Karabeg
6464cfa7f2 fix(logs): refresh logic to refresh logs details (#2958) 2026-01-23 17:22:33 -08:00
Waleed
7f4edc85ef fix(billing): handle missing userStats and prevent crashes (#2956)
* fix(billing): handle missing userStats and prevent crashes

* fix(billing): correct import path for getFilledPillColor

* fix(billing): add Number.isFinite check to lastPeriodCost
2026-01-23 14:45:11 -08:00
Siddharth Ganesan
efef91ece0 improvement(copilot): fast mode, subagent tool responses and allow preferences (#2955)
* Improvements

* Fix actions mapping

* Remove console logs
2026-01-23 13:03:05 -08:00
Waleed
64efeaa2e6 feat(admin): add credits endpoint to issue credits to users (#2954)
* feat(admin): add credits endpoint to issue credits to users

* fix(admin): use existing credit functions and handle enterprise seats

* fix(admin): reject NaN and Infinity in amount validation

* styling

* fix(admin): validate userId and email are strings
2026-01-23 11:33:13 -08:00
Waleed
9b72b52b33 feat(blog): v0.5 release post (#2953)
* feat(blog): v0.5 post

* improvement(blog): simplify title and remove code block header

- Simplified blog title from "Introducing Sim Studio v0.5" to "Introducing Sim v0.5"
- Removed language label header and copy button from code blocks for cleaner appearance

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* ack PR comments

* small styling improvements

* created system to create post-specific components

* updated componnet

* cache invalidation

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 09:07:53 -08:00
Emir Karabeg
1467862488 improvement(logs): trace span, details (#2952)
* improvement(action-bar): ordering

* improvement(logs): details, trace span
2026-01-22 19:50:20 -08:00
Waleed
7f2262857c improvement(kb): add document filtering, select all, and React Query migration (#2951)
* improvement(kb): add document filtering, select all, and React Query migration

* test(kb): update tests for enabledFilter and removed userId params

* fix(kb): remove non-null assertion, add explicit guard
2026-01-22 19:25:16 -08:00
Vikhyath Mondreti
1b309b50e6 fix(idempotency): add conflict target to atomicallyClaimDb query + remove redundant db namespace tracking (#2950)
* fix(idempotency): add conflict target to atomicallyClaimDb query

* delete needs to account for namespace

* simplify namespace filtering logic

* fix cleanup

* consistent target
2026-01-22 18:38:08 -08:00
Waleed
f765b83a26 chore(deps): bump posthog-js to 1.334.1 (#2948) 2026-01-22 18:06:05 -08:00
Vikhyath Mondreti
aa99db6fdd fix(subflows): tag dropdown + resolution logic (#2949)
* fix(subflows): tag dropdown + resolution logic

* fixes;

* revert parallel change
2026-01-22 17:57:55 -08:00
Waleed
e9c4251c1c v0.5.68: router block reasoning, executor improvements, variable resolution consolidation, helm updates (#2946)
* improvement(workflow-item): stabilize avatar layout and fix name truncation (#2939)

* improvement(workflow-item): stabilize avatar layout and fix name truncation

* fix(avatars): revert overflow bg to hardcoded color for contrast

* fix(executor): stop parallel execution when block errors (#2940)

* improvement(helm): add per-deployment extraVolumes support (#2942)

* fix(gmail): expose messageId field in read email block (#2943)

* fix(resolver): consolidate reference resolution  (#2941)

* fix(resolver): consolidate code to resolve references

* fix edge cases

* use already formatted error

* fix multi index

* fix backwards compat reachability

* handle backwards compatibility accurately

* use shared constant correctly

* feat(router): expose reasoning output in router v2 block (#2945)

* fix(copilot): always allow, credential masking (#2947)

* Fix always allow, credential validation

* Credential masking

* Autoload

* fix(executor): handle condition dead-end branches in loops (#2944)

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com>
2026-01-22 13:48:15 -08:00
Waleed
748793e07d fix(executor): handle condition dead-end branches in loops (#2944) 2026-01-22 13:30:11 -08:00
Siddharth Ganesan
91da7e183a fix(copilot): always allow, credential masking (#2947)
* Fix always allow, credential validation

* Credential masking

* Autoload
2026-01-22 13:07:16 -08:00
Waleed
ab09a5ad23 feat(router): expose reasoning output in router v2 block (#2945) 2026-01-22 12:43:57 -08:00
Vikhyath Mondreti
fcd0240db6 fix(resolver): consolidate reference resolution (#2941)
* fix(resolver): consolidate code to resolve references

* fix edge cases

* use already formatted error

* fix multi index

* fix backwards compat reachability

* handle backwards compatibility accurately

* use shared constant correctly
2026-01-22 12:38:50 -08:00
Waleed
4e4149792a fix(gmail): expose messageId field in read email block (#2943) 2026-01-22 11:46:34 -08:00
Waleed
9a8b591257 improvement(helm): add per-deployment extraVolumes support (#2942) 2026-01-22 11:35:23 -08:00
Waleed
f3ae3f8442 fix(executor): stop parallel execution when block errors (#2940) 2026-01-22 11:34:40 -08:00
Waleed
66dfe2c6b2 improvement(workflow-item): stabilize avatar layout and fix name truncation (#2939)
* improvement(workflow-item): stabilize avatar layout and fix name truncation

* fix(avatars): revert overflow bg to hardcoded color for contrast
2026-01-22 11:26:47 -08:00
Waleed
cc2be33d6b v0.5.67: loading, password reset, ui improvements, helm updates (#2928)
* fix(zustand): updated to useShallow from deprecated createWithEqualityFn (#2919)

* fix(logger): use direct env access for webpack inlining (#2920)

* fix(notifications): text overflow with line-clamp (#2921)

* chore(helm): add env vars for Vertex AI, orgs, and telemetry (#2922)

* fix(auth): improve reset password flow and consolidate brand detection (#2924)

* fix(auth): improve reset password flow and consolidate brand detection

* fix(auth): set errorHandled for EMAIL_NOT_VERIFIED to prevent duplicate error

* fix(auth): clear success message on login errors

* chore(auth): fix import order per lint

* fix(action-bar): duplicate subflows with children (#2923)

* fix(action-bar): duplicate subflows with children

* fix(action-bar): add validateTriggerPaste for subflow duplicate

* fix(resolver): agent response format, input formats, root level (#2925)

* fix(resolvers): agent response format, input formats, root level

* fix response block initial seeding

* fix tests

* fix(messages-input): fix cursor alignment and auto-resize with overlay (#2926)

* fix(messages-input): fix cursor alignment and auto-resize with overlay

* fixed remaining zustand warnings

* fix(stores): remove dead code causing log spam on startup (#2927)

* fix(stores): remove dead code causing log spam on startup

* fix(stores): replace custom tools zustand store with react query cache

* improvement(ui): use BrandedButton and BrandedLink components (#2930)

- Refactor auth forms to use BrandedButton component
- Add BrandedLink component for changelog page
- Reduce code duplication in login, signup, reset-password forms
- Update star count default value

* fix(custom-tools): remove unsafe title fallback in getCustomTool (#2929)

* fix(custom-tools): remove unsafe title fallback in getCustomTool

* fix(custom-tools): restore title fallback in getCustomTool lookup

Custom tools are referenced by title (custom_${title}), not database ID.
The title fallback is required for client-side tool resolution to work.

* fix(null-bodies): empty bodies handling (#2931)

* fix(null-statuses): empty bodies handling

* address bugbot comment

* fix(token-refresh): microsoft, notion, x, linear (#2933)

* fix(microsoft): proactive refresh needed

* fix(x): missing token refresh flag

* notion and linear missing flag too

* address bugbot comment

* fix(auth): handle EMAIL_NOT_VERIFIED in onError callback (#2932)

* fix(auth): handle EMAIL_NOT_VERIFIED in onError callback

* refactor(auth): extract redirectToVerify helper to reduce duplication

* fix(workflow-selector): use dedicated selector for workflow dropdown (#2934)

* feat(workflow-block): preview (#2935)

* improvement(copilot): tool configs to show nested props (#2936)

* fix(auth): add genericOAuth providers to trustedProviders (#2937)

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Emir Karabeg <78010029+emir-karabeg@users.noreply.github.com>
2026-01-21 22:53:25 -08:00
Waleed
376f7cb571 fix(auth): add genericOAuth providers to trustedProviders (#2937) 2026-01-21 22:44:30 -08:00
Vikhyath Mondreti
42159c23b9 improvement(copilot): tool configs to show nested props (#2936) 2026-01-21 20:02:59 -08:00
Emir Karabeg
2f0f246002 feat(workflow-block): preview (#2935) 2026-01-21 19:12:28 -08:00
Waleed
900d3ef9ea fix(workflow-selector): use dedicated selector for workflow dropdown (#2934) 2026-01-21 18:38:03 -08:00
Waleed
f3fcc28f89 fix(auth): handle EMAIL_NOT_VERIFIED in onError callback (#2932)
* fix(auth): handle EMAIL_NOT_VERIFIED in onError callback

* refactor(auth): extract redirectToVerify helper to reduce duplication
2026-01-21 18:34:49 -08:00
Vikhyath Mondreti
7cfdf46724 fix(token-refresh): microsoft, notion, x, linear (#2933)
* fix(microsoft): proactive refresh needed

* fix(x): missing token refresh flag

* notion and linear missing flag too

* address bugbot comment
2026-01-21 18:30:53 -08:00
Vikhyath Mondreti
d681451297 fix(null-bodies): empty bodies handling (#2931)
* fix(null-statuses): empty bodies handling

* address bugbot comment
2026-01-21 18:10:33 -08:00
Waleed
5987a6d060 fix(custom-tools): remove unsafe title fallback in getCustomTool (#2929)
* fix(custom-tools): remove unsafe title fallback in getCustomTool

* fix(custom-tools): restore title fallback in getCustomTool lookup

Custom tools are referenced by title (custom_${title}), not database ID.
The title fallback is required for client-side tool resolution to work.
2026-01-21 17:36:10 -08:00
Waleed
e2ccefb2f4 improvement(ui): use BrandedButton and BrandedLink components (#2930)
- Refactor auth forms to use BrandedButton component
- Add BrandedLink component for changelog page
- Reduce code duplication in login, signup, reset-password forms
- Update star count default value
2026-01-21 17:25:30 -08:00
Waleed
103b31a569 fix(stores): remove dead code causing log spam on startup (#2927)
* fix(stores): remove dead code causing log spam on startup

* fix(stores): replace custom tools zustand store with react query cache
2026-01-21 16:08:26 -08:00
Waleed
004e058353 fix(messages-input): fix cursor alignment and auto-resize with overlay (#2926)
* fix(messages-input): fix cursor alignment and auto-resize with overlay

* fixed remaining zustand warnings
2026-01-21 15:30:13 -08:00
Vikhyath Mondreti
5157f0bbb2 fix(resolver): agent response format, input formats, root level (#2925)
* fix(resolvers): agent response format, input formats, root level

* fix response block initial seeding

* fix tests
2026-01-21 14:55:23 -08:00
Waleed
8bbcf31b83 fix(action-bar): duplicate subflows with children (#2923)
* fix(action-bar): duplicate subflows with children

* fix(action-bar): add validateTriggerPaste for subflow duplicate
2026-01-21 14:54:29 -08:00
Waleed
9e814315dd fix(auth): improve reset password flow and consolidate brand detection (#2924)
* fix(auth): improve reset password flow and consolidate brand detection

* fix(auth): set errorHandled for EMAIL_NOT_VERIFIED to prevent duplicate error

* fix(auth): clear success message on login errors

* chore(auth): fix import order per lint
2026-01-21 14:42:14 -08:00
Waleed
0ea0256623 chore(helm): add env vars for Vertex AI, orgs, and telemetry (#2922) 2026-01-21 11:36:16 -08:00
Waleed
fb8868c854 fix(notifications): text overflow with line-clamp (#2921) 2026-01-21 10:20:21 -08:00
Waleed
ea4964052d fix(logger): use direct env access for webpack inlining (#2920) 2026-01-21 10:14:40 -08:00
Waleed
268e2f114f fix(zustand): updated to useShallow from deprecated createWithEqualityFn (#2919) 2026-01-21 09:47:48 -08:00
Vikhyath Mondreti
45371e521e v0.5.66: external http requests fix, ring highlighting 2026-01-21 02:55:39 -08:00
Vikhyath Mondreti
5988d0e46f fix(ring): duplicate should clear original block (#2916)
* fix(ring): duplicate should clear original block

* rename correctly
2026-01-21 02:40:58 -08:00
Vikhyath Mondreti
145db9d8c3 fix(http): options not parsed accurately (#2914)
* fix(http): options not parsed accurately

* fix lint

* remove boilerplate code'
2026-01-21 01:36:29 -08:00
Waleed
0ce0f98aa5 v0.5.65: gemini updates, textract integration, ui updates (#2909)
* fix(google): wrap primitive tool responses for Gemini API compatibility (#2900)

* fix(canonical): copilot path + update parent (#2901)

* fix(rss): add top-level title, link, pubDate fields to RSS trigger output (#2902)

* fix(rss): add top-level title, link, pubDate fields to RSS trigger output

* fix(imap): add top-level fields to IMAP trigger output

* improvement(browseruse): add profile id param (#2903)

* improvement(browseruse): add profile id param

* make request a stub since we have directExec

* improvement(executor): upgraded abort controller to handle aborts for loops and parallels (#2880)

* improvement(executor): upgraded abort controller to handle aborts for loops and parallels

* comments

* improvement(files): update execution for passing base64 strings (#2906)

* progress

* improvement(execution): update execution for passing base64 strings

* fix types

* cleanup comments

* path security vuln

* reject promise correctly

* fix redirect case

* remove proxy routes

* fix tests

* use ipaddr

* feat(tools): added textract, added v2 for mistral, updated tag dropdown (#2904)

* feat(tools): added textract

* cleanup

* ack pr comments

* reorder

* removed upload for textract async version

* fix additional fields dropdown in editor, update parser to leave validation to be done on the server

* added mistral v2, files v2, and finalized textract

* updated the rest of the old file patterns, updated mistral outputs for v2

* updated tag dropdown to parse non-operation fields as well

* updated extension finder

* cleanup

* added description for inputs to workflow

* use helper for internal route check

* fix tag dropdown merge conflict change

* remove duplicate code

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>

* fix(ui): change add inputs button to match output selector (#2907)

* fix(canvas): removed invite to workspace from canvas popover (#2908)

* fix(canvas): removed invite to workspace

* removed unused props

* fix(copilot): legacy tool display names (#2911)

* fix(a2a): canonical merge  (#2912)

* fix canonical merge

* fix empty array case

* fix(change-detection): copilot diffs have extra field (#2913)

* improvement(logs): improved logs ui bugs, added subflow disable UI (#2910)

* improvement(logs): improved logs ui bugs, added subflow disable UI

* added duplicate to action bar for subflows

* feat(broadcast): email v0.5 (#2905)

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Emir Karabeg <78010029+emir-karabeg@users.noreply.github.com>
2026-01-20 23:54:55 -08:00
Emir Karabeg
294b168ed9 feat(broadcast): email v0.5 (#2905) 2026-01-20 23:42:48 -08:00
Waleed
0dc2c1fe0d improvement(logs): improved logs ui bugs, added subflow disable UI (#2910)
* improvement(logs): improved logs ui bugs, added subflow disable UI

* added duplicate to action bar for subflows
2026-01-20 23:13:05 -08:00
Vikhyath Mondreti
fb90c4e9b1 fix(change-detection): copilot diffs have extra field (#2913) 2026-01-20 22:04:08 -08:00
Vikhyath Mondreti
0af96d06c6 fix(a2a): canonical merge (#2912)
* fix canonical merge

* fix empty array case
2026-01-20 21:58:13 -08:00
Vikhyath Mondreti
1d450578c8 fix(copilot): legacy tool display names (#2911) 2026-01-20 21:16:48 -08:00
Waleed
c6d408c65b fix(canvas): removed invite to workspace from canvas popover (#2908)
* fix(canvas): removed invite to workspace

* removed unused props
2026-01-20 20:29:53 -08:00
Waleed
16716ea26a fix(ui): change add inputs button to match output selector (#2907) 2026-01-20 19:24:59 -08:00
Waleed
563098ca0a feat(tools): added textract, added v2 for mistral, updated tag dropdown (#2904)
* feat(tools): added textract

* cleanup

* ack pr comments

* reorder

* removed upload for textract async version

* fix additional fields dropdown in editor, update parser to leave validation to be done on the server

* added mistral v2, files v2, and finalized textract

* updated the rest of the old file patterns, updated mistral outputs for v2

* updated tag dropdown to parse non-operation fields as well

* updated extension finder

* cleanup

* added description for inputs to workflow

* use helper for internal route check

* fix tag dropdown merge conflict change

* remove duplicate code

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-20 18:41:26 -08:00
Vikhyath Mondreti
1f1f015031 improvement(files): update execution for passing base64 strings (#2906)
* progress

* improvement(execution): update execution for passing base64 strings

* fix types

* cleanup comments

* path security vuln

* reject promise correctly

* fix redirect case

* remove proxy routes

* fix tests

* use ipaddr
2026-01-20 17:49:00 -08:00
Waleed
4afb245fa2 improvement(executor): upgraded abort controller to handle aborts for loops and parallels (#2880)
* improvement(executor): upgraded abort controller to handle aborts for loops and parallels

* comments
2026-01-20 15:40:37 -08:00
Vikhyath Mondreti
8344d68ca8 improvement(browseruse): add profile id param (#2903)
* improvement(browseruse): add profile id param

* make request a stub since we have directExec
2026-01-20 11:08:47 -08:00
Waleed
a26a1a9737 fix(rss): add top-level title, link, pubDate fields to RSS trigger output (#2902)
* fix(rss): add top-level title, link, pubDate fields to RSS trigger output

* fix(imap): add top-level fields to IMAP trigger output
2026-01-20 10:06:13 -08:00
Vikhyath Mondreti
689037a300 fix(canonical): copilot path + update parent (#2901) 2026-01-20 09:43:41 -08:00
Waleed
07f0c01dc4 fix(google): wrap primitive tool responses for Gemini API compatibility (#2900) 2026-01-20 09:27:45 -08:00
Waleed
dff1c9d083 v0.5.64: unsubscribe, search improvements, metrics, additional SSO configuration 2026-01-20 00:34:11 -08:00
Waleed
e4ad31bb6b fix(kb): align bulk chunk operation with API response (#2899)
* fix(kb): align bulk chunk operation with API response

* fix(kb): skip local state update for failed chunks

* fix(kb): correct errors type and refresh on partial failure
2026-01-20 00:24:50 -08:00
Waleed
84691fc873 improvement(modal): fixed popover issue in custom tools modal, removed the ability to update if no changes made (#2897)
* improvement(modal): fixed popover issue in custom tools modal, removed the ability to update if no changes made

* improvement(modal): fixed popover issue in custom tools modal, removed the ability to update if no changes made

* popover fixes, color picker keyboard nav, code simplification

* color standardization

* fix color picker

* set discard alert state when closing modal
2026-01-19 23:52:07 -08:00
Emir Karabeg
2daf34386e fix(copilot): ui/ux (#2891)
* feat(claude): added rules

* fix(copilot): chat loading; refactor(copilot): components, utils, hooks

* fix(copilot): options selection strikethrough

* fix(copilot): options render inside thinking

* fix(copilot): checkpoints, user-input; improvement(code): colors

* fix(copilot): scrolling, tool-call truncation, thinking ui

* fix(copilot): tool call spacing and shimmer/actions on previous messages

* improvement(copilot): queue

* addressed comments
2026-01-19 23:23:21 -08:00
Waleed
ac991d4b54 fix(sso): removed provider specific OIDC logic from SSO registration & deregistration scripts (#2896)
* fix(sso): updated registration & deregistration script for explicit support for Entra ID

* cleanup

* ack PR comment

* ack PR comment

* tested edge cases, ack'd PR comments

* remove trailing slash
2026-01-19 19:23:50 -08:00
Waleed
69614d2d93 improvement(kb): migrate manual fetches in kb module to use reactquery (#2894)
* improvement(kb): migrate manual fetches in kb module to use reactquery

* converted remaining manual kb fetches

* unwrap kb tags before API call, added more query invalidation for chunks

* added resetMutation calls after modal closes
2026-01-19 17:25:17 -08:00
Waleed
6cbadd7110 feat(api): added workflows api route for dynamic discovery (#2892)
* feat(api): added workflows api route for dynamic discovery

* added ability to edit parameter and workflow descriptions

* added new rate limit category, ack PR comments

* fix hasChanges logic

* added whitespace trimming before hasChanges check
2026-01-19 17:21:51 -08:00
Vikhyath Mondreti
9efd3d5b4c improvement(stats): should track mcp and a2a executions like other trigger types (#2895)
* improvement(stats): should track mcp and a2a executions like other trigger types

* update types
2026-01-19 16:29:37 -08:00
Siddharth Ganesan
e575ba2965 feat(settings): add debug mode for superusers (#2893)
* Superuser debug

* Fix

* update templates routes to use helper

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-19 16:28:43 -08:00
Siddharth Ganesan
5f45db4343 improvement(copilot): variables, conditions, router (#2887)
* Temp

* Condition and router copilot syntax updates

* Plan respond plan
2026-01-19 15:24:50 -08:00
Waleed
81cbfe7af4 feat(browseruse): upgraded browseruse endpoints to v2 (#2890) 2026-01-19 14:47:19 -08:00
Waleed
739341b08e improvement(router): add resizable textareas for router conditions (#2888) 2026-01-19 13:59:13 -08:00
Waleed
3c43779ba3 feat(search): added operations to search modal in main app, updated retrieval in docs to use RRF (#2889) 2026-01-19 13:57:56 -08:00
Waleed
1861f77283 feat(terminal): add fix in copilot for errors (#2885) 2026-01-19 13:42:34 -08:00
Vikhyath Mondreti
72c2ba7443 fix(linear): team selector in tool input (#2886) 2026-01-19 12:40:45 -08:00
Waleed
037dad6975 fix(undo-redo): preserve subblock values during undo/redo cycles (#2884)
* fix(undo-redo): preserve subblock values during undo/redo cycles

* added tests
2026-01-19 12:19:51 -08:00
Waleed
408597e12b feat(notifs): added block name to error notifications (#2883) 2026-01-19 09:54:19 -08:00
Waleed
932f8fd654 feat(mcp): updated mcp subblocks for mcp tools to match subblocks (#2882)
* feat(mcp): updated mcp subblocks for mcp tools to match subblocks

* updated trigger descriptions
2026-01-19 09:50:03 -08:00
Waleed
b4c2294e67 improvement(emails): update unsub page, standardize unsub process (#2881) 2026-01-18 20:42:04 -08:00
Vikhyath Mondreti
b09f683072 v0.5.63: ui and performance improvements, more google tools 2026-01-18 15:22:42 -08:00
Vikhyath Mondreti
1dbf92db3f fix(api): tool input parsing into table from agent output (#2879)
* fix(api): transformTable to map agent output to table subblock format

* fix api

* add test
2026-01-18 14:43:02 -08:00
Waleed
3a923648cb feat(ux): more explicit verbiage on some dialog menus, google drive updates, advanved to additional fields, remove general settings store sync in favor of tanstack (#2875)
* fix(verbiage): more explicit verbiage on some dialog menus, google drive updates, advanved to additional fields, remove general settings store sync in favor of tanstack

* updated docs

* nested tag dropdown, more well-defined nested outputs, keyboard nav for context menus, etc

* cleanup

* allow cannonical toggle even if depends on not satisfied

* remove smooth scroll in tag drop

* fix selection

* fix

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-18 13:40:59 -08:00
Vikhyath Mondreti
5e2468cfd3 impovement(slides): add missing properties definitions (#2877) 2026-01-18 12:35:58 -08:00
Vikhyath Mondreti
7c0f43305b fix(resolver): tool configs must take precedence (#2876) 2026-01-18 10:11:57 -08:00
Waleed
ee7572185a improvement(tools): added visibility for tools that were missing it, added new google and github tools (#2874)
* improvement(tools): added visibility for tools that were missing it, added new google tools

* fixed the name for google forms

* revert schema enrichers change

* fixed block ordering
2026-01-17 20:51:15 -08:00
Waleed
19a8daedf7 improvement(performance): used react scan to identify rerendering issues and react issues (#2873) 2026-01-17 19:20:52 -08:00
Vikhyath Mondreti
0fcd52683a improvement(tool-input): general abstraction to enrich agent context, reuse visibility helpers (#2872)
* add abstraction for schema enrichment, improve agent KB block experience for tags, fix visibility of subblocks

* cleanup code

* consolidate

* fix workflow tool react query

* fix deployed context propagation

* fix tests
2026-01-17 19:13:27 -08:00
Waleed
b8b20576d3 improvement(ui): modal style standardization, select drop improvement, duplication selection fixes (#2871)
* improvement(ui): modal style standardization, select drop improvement

* consolidation, fixed canvas issues

* more
2026-01-17 13:31:46 -08:00
Waleed
4b8534ebd0 feat(oauth): upgraded all generic oauth plugin providers to use unqiue account ids (#2870) 2026-01-17 13:09:54 -08:00
Waleed
f6960a4bd4 fix(wand): improved flickering for invalid JSON icon while streaming (#2868) 2026-01-17 12:43:22 -08:00
Vikhyath Mondreti
8740566f6a fix(block-resolver): path lookup check (#2869)
* fix(block-resolver): path lookup check

* remove comments
2026-01-17 12:17:55 -08:00
Vikhyath Mondreti
a8bb0db660 v0.5.62: webhook bug fixes, seeding default subblock values, block selection fixes 2026-01-16 20:27:06 -08:00
Waleed
5de7228dd9 improvement(avatar): use selection-update as the source of truth for presence, ignore other socket ops (#2866)
* improvement(avatar): use selection-update as the source of truth for presence, ignore other socket ops

* added logs
2026-01-16 20:17:07 -08:00
Vikhyath Mondreti
75898c69ed fix(start): seed initial subblock values on batch add (#2864) 2026-01-16 20:07:20 -08:00
Vikhyath Mondreti
b14672887b fix(sockets): webhooks logic removal from copilot ops (#2862)
* fix(sockets): dying on deployed webhooks

* fix edit workflow
2026-01-16 19:53:14 -08:00
Waleed
d024c1e489 fix(shift): fix shift select blue ring fading (#2863) 2026-01-16 19:52:51 -08:00
Waleed
d75ea37b3c chore(readme): updated readme (#2861) 2026-01-16 18:18:40 -08:00
Waleed
af82820a28 v0.5.61: webhook improvements, workflow controls, react query for deployment status, chat fixes, reducto and pulse OCR, linear fixes 2026-01-16 18:06:23 -08:00
Vikhyath Mondreti
fd23220cc3 fix(slack): tool params should be in line with block (#2860)
* env var pattern outside loop

* fix(slack): tool params should line up with block

* remove comments
2026-01-16 18:00:44 -08:00
Adam Gough
a8d81097fc fix(google-vault): error handling improvement and more params (#2735)
* new error throw and improvement

* fixed critical issues

* restore error thorwing

* restore

* added handler for vault

* updated docs

* restored

* removed google vault from executor

* updated translations

* updated docs

* fixed inputs and outputs

---------

Co-authored-by: aadamgough <adam@sim.ai>
Co-authored-by: waleed <walif6@gmail.com>
2026-01-16 17:59:17 -08:00
Waleed
3768c6379c feat(readme): added deepwiki to readme, consolidated utils (#2856)
* feat(readme): added deepwiki to readme, consolidated utils

* standardized all modals

* updated modal copy

* standardized modals

* streamlined all error msg patterns
2026-01-16 16:07:31 -08:00
Siddharth Ganesan
aa80116b99 fix(copilot): copilot edit router block accepts semantic handles (#2857)
* Fix copilot diff controls

* Fix router block for copilot

* Fix queue

* Fix lint

* Get block options and config for subflows

* Lint
2026-01-16 15:54:28 -08:00
Vikhyath Mondreti
78e4ca9d45 improvement(serializer): canonical subblock, serialization cleanups, schedules/webhooks are deployment version friendly (#2848)
* hide form deployment tab from docs

* progress

* fix resolution

* cleanup code

* fix positioning

* cleanup dead sockets adv mode ops

* address greptile comments

* fix tests plus more simplification

* fix cleanup

* bring back advanced mode with specific definition

* revert feature flags

* improvement(subblock): ui

* resolver change to make all var references optional chaining

* fix(webhooks/schedules): deployment version friendly

* fix tests

* fix credential sets with new lifecycle

* prep merge

* add back migration

* fix display check for adv fields

* fix trigger vs block scoping

---------

Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
2026-01-16 15:23:43 -08:00
Waleed
ce3ddb6ba0 improvement(deployed-mcp): added the ability to make the visibility for deployed mcp tools public, updated UX (#2853)
* improvement(deployed-mcp): added the ability to make the visibility for deployed mcp tools public, updated UX

* use reactquery

* migrated chats to use reactquery, upgraded entire deploymodal to use reactquery instead of manual state management

* added hooks for chat chats and updated callers to all use reactquery

* fix

* updated comments

* consolidated utils
2026-01-16 14:18:39 -08:00
Siddharth Ganesan
8361931cdf fix(copilot): fix copilot bugs (#2855)
* Fix edit workflow returning bad state

* Fix block id edit, slash commands at end, thinking tag resolution, add continue button

* Clean up autosend and continue options and enable mention menu

* Cleanup

* Fix thinking tags

* Fix thinking text

* Fix get block options text

* Fix bugs

* Fix redeploy

* Fix loading indicators

* User input expansion

* Normalize copilot subblock ids

* Fix handlecancelcheckpoint
2026-01-16 13:57:55 -08:00
Waleed
c863125c6b feat(workspace): added option to leave workspace (#2854) 2026-01-16 12:38:11 -08:00
Waleed
fa63af9222 fix(queries): remove more remaining manual state management and refetching in favor of reactquery (#2852) 2026-01-16 12:01:28 -08:00
Waleed
dba57998d2 improvement(posthog): improve posthog config to be more lightweight (#2851) 2026-01-16 08:47:06 -08:00
Waleed
583f5c4cbb fix(webflow): fix collection & site dropdown in webflow triggers (#2849)
* fix(webflow): fix collection & site dropdown in webflow triggers

* added form submission trigger to webflow

* fix(webflow): added form submission trigger and scope

* fixed function signatures
2026-01-16 08:22:09 -08:00
Emir Karabeg
6ff68b39ce Improvement: subblocks (#2850)
* improvement(panel): increased default width

* improvement: subblocks

* fix: generate, mcp, ring
2026-01-15 23:02:51 -08:00
Waleed
55700b9bf4 improvement(security): added input validation for airtable, lemlist, and more tools to protect against SSRF (#2847) 2026-01-15 19:33:29 -08:00
Waleed
51e376847f fix(linear): updated linear tools to enforce only required fields per api spec (#2845) 2026-01-15 18:58:11 -08:00
Waleed
feb994c819 improvement(presence): show presence for the same user in another tab, fix z-index of multiplayer cursor to fall behind panel,terminal,sidebar but above blocks, improved connection detection (#2844)
* improvement(presence): show presence for the same user in another tab, fix z-index of multiplayer cursor to fall behind panel,terminal,sidebar but above blocks, improved connection detection

* upsert users into presence list
2026-01-15 18:46:46 -08:00
Waleed
12470a630c feat(ocr): added reducto and pulse for OCR (#2843)
* feat(ocr): added reducto and pulse for OCR

* ack comments
2026-01-15 18:30:39 -08:00
Emir Karabeg
b813bf7f27 improvement: workflow, blocks, preview, avatars, output-select (#2840)
* improvement(workflow): ui/ux, refactors, optimizations

* improvement: blocks, preview, avatars

* improvement(output-select): ui

* update API endpoint picker to match output selector

* improvement: subflow ui/ux

---------

Co-authored-by: waleed <walif6@gmail.com>
2026-01-15 17:42:59 -08:00
Waleed
81cc88b2e2 feat(context-menu): added context menu to dead sidebar space and usage indicator (#2841) 2026-01-15 16:54:33 -08:00
Waleed
87e6057033 improvement(chat): partialize chat store to only persist image URL instead of full image in floating chat (#2842) 2026-01-15 16:54:24 -08:00
Vikhyath Mondreti
f1796d13df fix(start): permission check for executor 2026-01-15 16:43:06 -08:00
Waleed
6f469a7f37 improvement(permissions): added ability to auto-add new org members to existing permission group, disallow disabling of start block (#2836)
* improvement(permissions): added ability to auto-add new org members to existing permission group, disallow disabling of start block

* ran migrations

* add deploy modal tabs config to perm groups

* fix ordering of access control listings

* prep staging merge

* regen migrations

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-15 15:09:00 -08:00
Waleed
a35f6eca03 improvement(tools): use react query to fetch child workflow schema, avoid refetch and duplicated utils, consolidated utils and testing mocks (#2839)
* improvement(tools): use react query to fetch child workflow schema, avoid refetch and duplicated utils

* consolidated utils & testing mocks
2026-01-15 13:25:22 -08:00
Waleed
1cc489e544 feat(workflow-controls): added action bar for workflow controls (#2767)
* feat(workflow-controls): added action bar for picker/hand/undo/redo/zoom workflow controls, added general setting to disable

* added util for fit to zoom that accounts for sidebar, terminal, and panel

* ack PR comments

* remove dead state variable, add logs

* improvement(ui/ux): action bar, panel, tooltip, dragging, invite modal

* added fit to view in canvas context menu

* fix(theme): dark mode flash

* fix: duplicate fit to view

* refactor: popovers; improvement: notifications, diff controls, action bar

* improvement(action-bar): ui/ux

* refactor(action-bar): renamed to workflow controls

* ran migrations

* fix: deleted migration

---------

Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
2026-01-15 13:25:00 -08:00
Vikhyath Mondreti
e499cc4f82 improvement(webhooks): lifecycle management with external providers, remove save configuration (#2831)
* fix(webhooks): lifecycle code accuracy

* remove save configuration button

* remove useless instruction

* address greptile comments

* fix lint

* on undeploy cleanup webhooks
2026-01-15 12:42:05 -08:00
Waleed
5e44357b9f improvement(snapshot): show subblocks for trigger only blocks in frozen canvas (#2838)
* improvement(snapshot): show subblocks for trigger only blocks in frozen canvas

* ack comment
2026-01-15 10:34:38 -08:00
Waleed
debcd76019 improvement(slack): updated docs to include information for slack marketplace submission (#2837) 2026-01-15 10:01:00 -08:00
Waleed
4372841797 v0.5.60: invitation flow improvements, chat fixes, a2a improvements, additional copilot actions 2026-01-15 00:02:18 -08:00
Waleed
929d0d01fd feat(sheets): added sheet selector for microsoft excel and google sheets tools (#2835)
* feat(sheets): added sheet selector for microsoft excel and google sheets tools

* upgrade generate docs script

* updated tests

* added sheet-selector to tool-input

* added cursor docs
2026-01-15 00:01:31 -08:00
Vikhyath Mondreti
e53538d079 fix(drag): read perms prevent drag (#2834) 2026-01-14 23:25:31 -08:00
Vikhyath Mondreti
d4c171c6d7 fix(sortOrder): initial ordering must be deterministic (#2833)
* fix(sortOrder): initial ordering must be deterministic

* fix initial ordering issue

* add created at to child item
2026-01-14 22:21:49 -08:00
Waleed
26d0799d22 fix(popover): fix frozen workspace popover (#2832) 2026-01-14 22:08:10 -08:00
Waleed
45bd1e8cd7 feat(starter): in start block input format, don't prevent deletion if only one field remaining, just clear form (#2830) 2026-01-14 21:24:02 -08:00
Waleed
85d6e3e3bd fix(misc): added trace spans back to notifications for webhooks, updated verification code for users signing in with email, updated welcome email (#2828)
* added back trace spans to notifications

* fixed double verification code

* fix dashboard

* updated welcome email

* added link to cal for team

* update dashboard stats route

* added react grab URL to CSP if FF is enabled, removed dead db hook

* fix failing test

* ensure MCP add server tool is centered

* updated A2A copy button and MCP location, and default description matching

* updated button on chat page

* added vite version override

* fix
2026-01-14 21:17:20 -08:00
Siddharth Ganesan
ccf268595e improvement(copilot): update copilot to match copilot repo (#2829)
* Ux

* Fix lint

* Clean up model options

* Codex
2026-01-14 20:52:49 -08:00
Vikhyath Mondreti
5eca660c5c improvement(langsmith): add wand for batch ingestion schemas (#2827) 2026-01-14 19:50:35 -08:00
Waleed
3db9ad2d95 improvement(emails): update email footer links to link to sim.ai/provider instead of direct provider links (#2826) 2026-01-14 18:53:11 -08:00
Waleed
4195cfe1ff fix(otp): send welcome email even when user signs up via email/pass along with oauth providers (#2825) 2026-01-14 18:46:18 -08:00
Waleed
212933746e improvement(langsmith): ugpraded langsmith to use tool names directly in dropdown (#2824) 2026-01-14 18:46:06 -08:00
Waleed
5af72ea22f feat(dashboard): added stats endpoint to compute stats on server side and avoid limit (#2823)
* feat(dashboard): added stats endpoint to compute stats on server side and avoid limit

* updated query
2026-01-14 18:41:46 -08:00
Waleed
4899c28421 fix(notifications): consolidate notification utils, update email styling (#2822)
* fix(notifications): consolidate notification utils, update email styling

* fixed duplicate types
2026-01-14 18:35:51 -08:00
Vikhyath Mondreti
2cee30ff15 feat(langsmith): add langsmith tools for logging, output selector use tool-aware listing (#2821)
* feat(langsmith): add langsmith tools for logging, output selector use tool-aware listing

* fix

* fix docs

* fix positioning of outputs

* fix docs script
2026-01-14 16:14:24 -08:00
Waleed
41f9374b5c fix(agent-tools): added special handling for workflow tool in agent tool input, added react grab and feature flag (#2820)
* fix(agent-tools): added special handling for workflow tool in agent tool input, added react grab

* FF react grab

* ack comments

* updated to account for workflow input tool on top of just workflow as well
2026-01-14 15:53:42 -08:00
Vikhyath Mondreti
6c8c3d6368 feat(reorder): allow workflow/folder reordering (#2818)
* feat(reorder): allow workflow/folder reordering

* progress

* fix edge cases

* add migration

* fix bun lock

* updated to use brand tertiary color, allow worfklows to be dropped above/below folders at the same level

* cahnged color, removed flicker on folder container

* optimized

* ack pr comments

* removed empty placeholder images for drag, removed redundant local sanitization helper

---------

Co-authored-by: waleed <walif6@gmail.com>
2026-01-14 15:52:27 -08:00
Vikhyath Mondreti
3f1dccd6aa fix(batch-add): on batch add persist subblock values (#2819)
* fix(batch-add): on batch add persist subblock values

* consolidate merge subblock

* consolidate more code
2026-01-14 13:01:26 -08:00
Waleed
468ec2ea81 fix(terminal-colors): change algo to compute colors based on hash of execution id and pointer from bottom (#2817) 2026-01-14 12:06:02 -08:00
Waleed
d7e0d9ba43 fix(i18n): update translations action to run once per week on sunday (#2816) 2026-01-14 11:23:26 -08:00
Waleed
51477c12cc fix(terminal): pop all entries from a single execution when the limit is exceeded (#2815) 2026-01-14 11:05:38 -08:00
Waleed
a3535639f1 fix(copilot): rewrote user input popover to optimize UX (#2814)
* fix(copilot): rewrote user input popover to optimize UX

* cleanup

* make keyboard and moues share state

* escape goes one level up on slash popover
2026-01-14 11:04:53 -08:00
Lakee Sivaraya
d5bd97de32 feat(tinybird): added tinybird block (#2781) 2026-01-14 00:25:54 -08:00
Siddharth Ganesan
bd7009e316 fix(copilot): commands (#2811) 2026-01-13 22:48:21 -08:00
Waleed
4f04b1efea feat(terminal): migrate from zustand for console terminal logs to indexedDb, incr limit from 5mb to ~GBs (#2812)
* feat(terminal): migrate from zustand for console terminal logs to indexedDb, incr limit from 5mb to ~GBs

* ack PR comments
2026-01-13 22:42:15 -08:00
Vikhyath Mondreti
258e96d6b5 improvement(pricing): drop agent multiplier in docs, change base exec cost 2026-01-13 22:03:19 -08:00
Waleed
4b026ad54d fix(a2a): added file data part and data data part to a2a agents (#2805)
* fix(a2a): added file data part and data data part to a2a agents

* removed unused streaming tool

* ack comment
2026-01-13 21:08:00 -08:00
Waleed
f6b7c15dc4 improvement(oauth): added random identifier in unused accountId to bypass betterauth unique constraint (#2807)
* improvement(oauth): added random identifier in unnused accountId to bypass betterauth unique constraint

* ack pr comments
2026-01-13 21:01:42 -08:00
Vikhyath Mondreti
70ed19fcdb fix(chat): remove special handling for non-streaming (#2808) 2026-01-13 20:43:37 -08:00
Waleed
d6e4c91e81 fix(invitations): preserve tokens after error (#2806) 2026-01-13 20:10:44 -08:00
Vikhyath Mondreti
e3fa40af11 fix(sockets): redrawing edges should not lead to socket ops (#2804)
* fix(sockets): redrawing edges should not lead to socket ops

* consolidate
2026-01-13 18:45:41 -08:00
Waleed
6e0055f847 feat(slack): added get message by timestamp and get thread tool (#2803)
* feat(slack): added get message tool

* added get thread
2026-01-13 18:37:06 -08:00
Vikhyath Mondreti
ebbe67aae3 fix(triggers): cleanup trigger outputs formatting, fix display name issues (#2801)
* fix(triggers): package lemlist data, cleanup trigger outputs formatting, fix display name issues

* cleanup trigger outputs

* fix tests

* more test fixes

* remove branch field for ones where it's not relevant

* remove branch from unrelated ops
2026-01-13 17:48:19 -08:00
Waleed
2b49d15ec8 fix(comparison): add condition to prevent duplicate identical edges (#2799)
* fix)comparison): add condition to prevent duplicate identical edges, ignore from workflow change detection

* fix failing test

* added back store check
2026-01-13 17:17:23 -08:00
Siddharth Ganesan
3d037c9b74 fix(executor): pattern match more errors to prevent swallow (#2802) 2026-01-13 17:12:31 -08:00
Siddharth Ganesan
eb52f69efd improvement(schedule): default schedule timezone (#2800) 2026-01-13 16:31:18 -08:00
Siddharth Ganesan
64b3f98488 feat(copilot): add commands (#2797)
* Slash commands v0

* Web tools

* Web

* Fix popover

* Fix commands ui

* Fix for context mentions too

* Improvem tool names for options and config

* Fix thinking text scroll

* Fix ishosted

* Ui

* Ui

* Subagent parallelization

* Fix ui

* Fix lint

* Fix superagent

* Dont collapse info and super
2026-01-13 16:23:39 -08:00
Waleed
5e8c843241 v0.5.59: a2a support, documentation 2026-01-13 13:21:21 -08:00
Waleed
4be420311c fix(a2a): removed deployment constraint for redeploying a2a workflows (#2796)
* fix(a2a): removed deployment constraint for redeploying a2a workflows

* updated A2A tab copy state

* consolidated trigger types const
2026-01-13 13:19:57 -08:00
Waleed
b49ed2fcd9 feat(export): support maintenance of nested folder structure on import/export, added folder export admin route (#2795)
* feat(export): support maintenance of nested folder structure on import/export

* consolidated utils, added admin routes

* remove default tags from A2A
2026-01-13 12:26:41 -08:00
Waleed
837405e1ec chore(docs): update sim references in docs (#2792) 2026-01-13 11:51:43 -08:00
Waleed
2bc403972c feat(a2a): added a2a protocol (#2784)
* feat(a2a): a2a added

* feat(a2a): added a2a protocol

* remove migrations

* readd migrations

* consolidated permissions utils

* consolidated tag-input, output select -> combobox, added tags for A2A

* cleanup up utils, share same deployed state as other tabs

* ack PR comments

* more

* updated code examples

* solely rely on tanstack query to vend data and invalidate query key's, remove custom caching

---------

Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
2026-01-13 11:43:02 -08:00
Vikhyath Mondreti
40a066f39c improvement(FF): CI check to prevent hardcoding of FFs (#2790)
* improvement(FF): CI check to prevent hardcoding of FFs

* revert test change

* add FF lint checks
2026-01-13 10:47:32 -08:00
Waleed
c9068d043e chore(readme): trim readme, add more envvar info (#2791) 2026-01-13 10:31:25 -08:00
Waleed
048eddd468 fix(ff): add back condition for isHosted FF (#2789)
* fix(ff): add back condition for isHosted FF

* updated callers to use getBaseUrl()
2026-01-13 08:38:19 -08:00
Waleed
7bf3d73ee6 v0.5.58: export folders, new tools, permissions groups enhancements 2026-01-13 00:56:59 -08:00
Waleed
6717ce89f4 fix(slack): remove duplicate effect that cleared subblocks on cred change (#2788) 2026-01-13 00:47:17 -08:00
Vikhyath Mondreti
a05003a2d3 feat(integrations): claude skills to add integrations, lemlist trigger + tools, remove test webhook url (#2785)
* feat(integrations): claude skills to add integrations, lemlist trigger + tools, remove test webhook url

* fix tests

* fix tools

* add more details to skill

* more details

* address greptile comments
2026-01-12 22:18:50 -08:00
Waleed
46417ddb8c feat(invitations): added FF to disable invitations, added to permission groups, added workspace members admin endpoints (#2783)
* feat(invitations): added FF to disable invitations, added to permission groups, added workspace members admin endpoints

* fix failing tests
2026-01-12 19:33:43 -08:00
Vikhyath Mondreti
b6cbee2464 improvement(block-outputs): display metadata properties destructured (#2772)
* improvement(block-outputs):display metadata properties destructured

* add back icons

* fix google calendar

* reuse versioned tool selector

* fix null fields

* github optionality

* fix notion

* review stripe tools metadata

* fix optional tools + types

* fix docs type

* add db row tool + fix copilot versioning recognition
2026-01-12 18:36:21 -08:00
Vikhyath Mondreti
91ed5338cb improvement(byok): make available for all plans (#2782)
* improvement(byok): make available for all plans

* update docs

* address greptile comments
2026-01-12 17:23:47 -08:00
Siddharth Ganesan
d55072a45f feat(copilot): add context7 (#2779)
* Add context7

* Fix edit diff block ring color

* Remove server side impl

* Fix duplicated message on edit old message

* Tables in markdown
2026-01-12 17:08:47 -08:00
Waleed
684ad5aeec feat(tool): added introspection tools for all db integrations (#2780)
* feat(tool): added introspection tools for all db integrations

* added sanitization for sql schema

* ack pr comments, with clarifying comments

* moved util
2026-01-12 13:30:55 -08:00
Waleed
a3dff1027f feat(tools): added workflow tools to agent tools dropdown for discoverability, enforce perms on client for redeploying via the agent (#2778)
* feat(tools): added workflow tools to agent tools dropdown for discoverability, enforce perms on client for redeploying via the agent

* added perms enforcement to workflow block header as well
2026-01-12 11:54:01 -08:00
Waleed
0aec9ef571 feat(export): added the ability to export workflow (#2777)
* feat(export): added the ability to export workflow

* improvement(import): loading animation

* fixed flicker on importing multiple workflows

* ack pr comments

* standardized import/export hooks

* upgraded turborepo

* cleaned up

---------

Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
2026-01-12 11:25:00 -08:00
Waleed
cb4db20a5f fix(color-picker): confirm color change before updating workflow color (#2776) 2026-01-11 18:47:48 -08:00
Vikhyath Mondreti
7ffc11a738 v0.5.57: subagents, context menu improvements, bug fixes 2026-01-11 11:38:40 -08:00
Waleed
4941b5224b fix(resize): fix subflow resize on drag, children deselected in subflow on drag (#2771)
* fix(resize): fix subflow resize on drag, children deselected in subflow on drag

* ack PR comments

* fix copy-paste subflows deselecting children

* ack comments
2026-01-11 11:28:47 -08:00
Waleed
7f18d96d32 feat(popover): add expandOnHover, added the ability to change the color of a workflow icon, new workflow naming convention (#2770)
* feat(popover): add expandOnHover, added the ability to change the color of a workflow icon

* updated workflow naming conventions
2026-01-10 21:30:34 -08:00
Siddharth Ganesan
e347486f50 fix(copilot): fix copilot chat loading (#2769)
* Fix loading

* Fix Lint

* Scroll stickiness

* Scroll stickiness

* improvement: diff controls and notifications positioning

* feat(copilot): editable input component

---------

Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
2026-01-10 18:24:21 -08:00
Waleed
e21cc1132b fix(subflow): updated subflow border to match block border (#2768) 2026-01-10 17:40:52 -08:00
Waleed
ab32a19cf4 fix(tag-input): add onInputChange to clear errors when new text is entered (#2765)
* fix(tag-input): add onInputChange to clear errors when new text is entered

* added paste case too
2026-01-10 16:48:57 -08:00
Waleed
ead2413b95 fix(context-menu): make divider on context menu aware of available options (#2766) 2026-01-10 14:06:51 -08:00
Vikhyath Mondreti
9a16e7c20f improvement(response): only allow singleton (#2764)
* improvement(response): only allow singleton

* respect singleton triggers and blocks in copilot

* don't show dup button for response

* fix error message
2026-01-10 12:16:32 -08:00
Siddharth Ganesan
283a521614 feat(copilot): subagents (#2731)
* fix(helm): add custom egress rules to realtime network policy (#2481)

The realtime service network policy was missing the custom egress rules section
that allows configuration of additional egress rules via values.yaml. This caused
the realtime pods to be unable to connect to external databases (e.g., PostgreSQL
on port 5432) when using external database configurations.

The app network policy already had this section, but the realtime network policy
was missing it, creating an inconsistency and preventing the realtime service
from accessing external databases configured via networkPolicy.egress values.

This fix adds the same custom egress rules template section to the realtime
network policy, matching the app network policy behavior and allowing users to
configure database connectivity via values.yaml.

* Add subagents

* Edit, plan, debug subagents

* Tweaks

* Message queue

* Many subagents

* Fix bugs

* Trigger request

* Overlays

* Diff in chat

* Remove context usage code

* Diff view in chat

* Options

* Lint

* Fix rendering of edit subblocks

* Add deploy mcp tools

* Add evaluator subagent

* Editor component

* Options select

* Fixes to options

* Fix spacing between options

* Subagent rendering

* Fix previews

* Plan

* Streaming

* Fix thinking scroll

* Renaming

* Fix thinking text

* Persist and load chats properly

* Diff view

* Fix lint

* Previous options should not be selectable

* Enable images

* improvement(copilot): ui/ux

* improvement(copilot): diff controls

* Fix ops bug

* Fix ops

* Stuff

* Fix config

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Martin Yankov <23098926+Lutherwaves@users.noreply.github.com>
Co-authored-by: Emir Karabeg <78010029+emir-karabeg@users.noreply.github.com>
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
Co-authored-by: Adam Gough <77861281+aadamgough@users.noreply.github.com>
Co-authored-by: aadamgough <adam@sim.ai>
Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
2026-01-10 11:44:04 -08:00
Vikhyath Mondreti
92fabe785d fix(perms): copilot checks undefined issue (#2763) 2026-01-10 11:23:35 -08:00
Siddharth Ganesan
3ed177520a fix(router): fix router ports (#2757)
* Fix router block

* Fix autoconnect edge for router

* Fix lint

* router block error path decision

* improve router prompt

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-10 11:22:11 -08:00
Waleed
be578e2ed7 v0.5.56: batch operations, access control and permission groups, billing fixes 2026-01-10 00:31:34 -08:00
Waleed
baa54b4c97 feat(docs): added circleback docs (#2762) 2026-01-10 00:30:49 -08:00
Waleed
a11d452d7b fix(build): fixed circular dependencies (#2761) 2026-01-10 00:10:20 -08:00
Waleed
6262503b89 feat(deployed-form): added deployed form input (#2679)
* feat(deployed-form): added deployed form input

* styling consolidation, finishing touches on form

* updated docs

* remove unused files with knip

* added more form fields

* consolidated more test utils

* remove unused/unneeded zustand stores, refactored stores for consistency

* improvement(files): uncolorized plan name

* feat(emcn): button-group

* feat(emcn): tag input, tooltip shortcut

* improvement(emcn): modal padding, api, chat, form

* fix: deleted migrations

* feat(form): added migrations

* fix(emcn): tag input

* fix: failing tests on build

* add suplementary hover and fix bg color in date picker

* fix: build errors

---------

Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
2026-01-09 23:42:21 -08:00
Waleed
67440432bf fix(ops): fix subflow resizing on exit (#2760)
* fix(sockets): broadcast handles and enabled/disabled state

* made all ops batched, removed all individual ops

* fix subflow resizing on exit

* removed unused custom event

* fix failing tests, update testing

* fix test mock
2026-01-09 22:35:03 -08:00
Vikhyath Mondreti
47eb060311 feat(enterprise): permission groups, access control (#2736)
* feat(permission-groups): integration/model access controls for enterprise

* feat: enterprise gating for BYOK, SSO, credential sets with org admin/owner checks

* execution time enforcement of mcp and custom tools

* add admin routes to cleanup permission group data

* fix not being on enterprise checks

* separate out orgs from billing system

* update the docs

* add custom tool blockers based on perm configs

* add migrations

* fix

* address greptile comments

* regen migrations

* fix default model picking based on user config

* cleaned up UI
2026-01-09 20:16:22 -08:00
Adam Gough
fd76e98f0e improvement(wand): added more wands (#2756)
* added wand configs

* fixed greptile comments
2026-01-09 18:41:51 -08:00
Waleed
1dbd16115f feat(sidebar): context menu for nav items in sidebar, toolbar blocks, added missing docs for various blocks and triggers (#2754)
* feat(sidebar): context menu for nav items in sidebar

* added toolbar context menu, fixed incorrect access pattern in old context menus and added docs for missing blocks

* fixed links
2026-01-09 17:50:10 -08:00
Vikhyath Mondreti
38e827b61a fix(docs): new router (#2755)
* fix(docs): new router

* update image
2026-01-09 17:37:04 -08:00
Waleed
1f5e8a41f8 fix(tools): fixed workflow tool for agent to respect user provided params, inject at runtime like all other tools (#2750)
* fix(tools): fixed wrokflow tool for agent to respect user provided params, inject at runtime like all other tools

* ack comments

* remove redunant if-else

* added tests
2026-01-09 17:12:58 -08:00
Adam Gough
796f73ee01 improvement(google-drive) (#2752)
* expanded metadata fields for google drive

* added tag dropdown support

* fixed greptile

* added utils func

* removed comments

* updated docs

* greptile comments

* fixed output schema

* reverted back to bas64 string
2026-01-09 16:56:07 -08:00
Waleed
d3d6012d5c fix(tools): updated memory block to throw better errors, removed deprecated posthog route, remove deprecated templates & console helpers (#2753)
* fix(tools): updated memory block to throw better errors, removed deprecated posthog route, remove deprecated templates & console helpers

* remove isDeployed in favor of deploymentStatus

* ack PR comments
2026-01-09 16:53:37 -08:00
Vikhyath Mondreti
860610b4c2 improvement(billing): team upgrade + session management (#2751)
* improvement(billng): team upgrade + session management

* remove comments

* session updates should be atomic

* make consistent for onSubscritionUpdate

* plan upgrade to refresh session

* fix var name

* remove dead code

* preserve params
2026-01-09 16:36:45 -08:00
Waleed
05bbf34265 improvement(canvas): add multi-block select, add batch handle, enabled, and edge operations (#2738)
* improvement(canvas): add multi-block select, add batch handle, enabled, and edge operations

* feat(i18n): update translations (#2732)

Co-authored-by: icecrasher321 <icecrasher321@users.noreply.github.com>

* don't allow flip handles for subflows

* ack PR comments

* more

* fix missing handler

* remove dead subflow-specific ops

* remove unused code

* fixed subflow ops

* keep edges on subflow actions intact

* fix subflow resizing

* fix remove from subflow bulk

* improvement(canvas): add multi-block select, add batch handle, enabled, and edge operations

* don't allow flip handles for subflows

* ack PR comments

* more

* fix missing handler

* remove dead subflow-specific ops

* remove unused code

* fixed subflow ops

* fix subflow resizing

* keep edges on subflow actions intact

* fixed copy from inside subflow

* types improvement, preview fixes

* fetch varible data in deploy modal

* moved remove from subflow one position to the right

* fix subflow issues

* address greptile comment

* fix test

* improvement(preview): ui/ux

* fix(preview): subflows

* added batch add edges

* removed recovery

* use consolidated consts for sockets operations

* more

---------

Co-authored-by: icecrasher321 <icecrasher321@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
2026-01-09 14:48:23 -08:00
Waleed
753600ed60 feat(i18n): update translations (#2749)
Co-authored-by: icecrasher321 <icecrasher321@users.noreply.github.com>
2026-01-09 14:11:57 -08:00
Vikhyath Mondreti
4da43d937c improvement(docs): multiplier dropped to 1.4 (#2748) 2026-01-09 11:41:04 -08:00
Waleed
9502227fd4 fix(sso): add missing deps to db container for running script (#2746) 2026-01-09 09:42:13 -08:00
Waleed
f415e5edc4 v0.5.55: polling groups, bedrock provider, devcontainer fixes, workflow preview enhancements 2026-01-08 23:36:56 -08:00
Adam Gough
13981549d1 fix(grain): grain trigger update (#2739)
* grain trigger new requirements

* removed comment

* made it generic for all triggers

* fire only for specific trigger type

* removed comments
2026-01-08 23:10:11 -08:00
Waleed
554dcdf062 improvement(execution-snapshot): enhance workflow preview in logs and deploy modal (#2742)
* added larger live deployment preview

* edited subblock UI

* removed comments

* removed carrot

* updated styling to match existing subblocks

* enriched workflow preview

* fix connetion in log preview

* cleanup

* ack PR comments

* more PR comments

* more

* cleanup

* use reactquery cache in deploy modal

* ack comments

* ack PR comment

---------

Co-authored-by: aadamgough <adam@sim.ai>
2026-01-08 23:04:54 -08:00
Adam Gough
6b28742b68 fix(linear): missing params (#2740)
* added missing params

* fixed linear bugs
2026-01-08 20:42:09 -08:00
Vikhyath Mondreti
e5c95093f6 improvement(autoconnect): click to add paths also autoconnect (#2737) 2026-01-08 18:16:15 -08:00
Waleed
b87af80bff feat(i18n): update translations (#2732)
Co-authored-by: icecrasher321 <icecrasher321@users.noreply.github.com>
2026-01-08 18:14:00 -08:00
Vikhyath Mondreti
c2180bf8a0 improvement(enterprise): feature flagging + runtime checks consolidation (#2730)
* improvement(enterprise): enterprise checks code consolidation

* update docs

* revert isHosted check

* add unique index to prevent multiple orgs per user

* address greptile comments

* ui bug
2026-01-08 13:53:22 -08:00
Waleed
fdac4314d2 fix(chat): update stream to respect all output select objects (#2729) 2026-01-08 11:54:07 -08:00
Waleed
a54fcbc094 improvement(auth): added ability to inject secrets to kubernetes, server-side ff to disable email registration (#2728)
* improvement(auth): added ability to inject secrets to kubernetes, server-side ff to disable email registration

* consolidated telemetry events

* comments cleanup

* ack PR comment

* refactor to use createEnvMock helper instead of local mocks
2026-01-08 11:09:35 -08:00
Waleed
05904a73b2 feat(i18n): update translations (#2721)
Co-authored-by: icecrasher321 <icecrasher321@users.noreply.github.com>
2026-01-08 10:30:53 -08:00
Lakshman Patel
1b22d2ce81 fix(devcontainer): use bunx for concurrently command (#2723) 2026-01-07 21:20:29 -08:00
Waleed
26dff7cffe feat(bedrock): added aws bedrock as a model provider (#2722) 2026-01-07 20:08:03 -08:00
Vikhyath Mondreti
020037728d feat(polling-groups): can invite multiple people to have their gmail/outlook inboxes connected to a workflow (#2695)
* progress on cred sets

* fix credential set system

* return data to render credential set in block preview

* progress

* invite flow

* simplify code

* fix ui

* fix tests

* fix types

* fix

* fix icon for outlook

* fix cred set name not showing up for owner

* fix rendering of credential set name

* fix outlook well known folder id resolution

* fix perms for creating cred set

* add to docs and simplify ui

* consolidate webhook code better

* fix tests

* fix credential collab logic issue

* fix ui

* fix lint
2026-01-07 17:49:40 -08:00
Waleed
13a6e6c3fa v0.5.54: seo, model blacklist, helm chart updates, fireflies integration, autoconnect improvements, billing fixes 2026-01-07 16:09:45 -08:00
Vikhyath Mondreti
cb12ceb82c fix(preproc-errors): should not charge base execution cost in this case (#2719)
* fix(preproc-errors): should not charge base execution cost in this case

* remove comment
2026-01-07 15:32:37 -08:00
Waleed
0f32310ba6 feat(i18n): update translations (#2717)
* feat(i18n): update translations

* fixed chinese docs

---------

Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2026-01-07 14:28:58 -08:00
Vikhyath Mondreti
730ddf5a66 ui improvements for deploy mcp (#2718) 2026-01-07 14:25:03 -08:00
Waleed
ef4bec2c37 improvement(context-menu): added awareness for chat and variables being open, fixed select calculation to match height calculation for selecting multiple blocks (#2715) 2026-01-07 13:40:53 -08:00
Waleed
2bd27f9a4d feat(fireflies): added fireflies tools and trigger (#2713)
* feat(fireflies): added fireflies tools and trigger

* finished fireflies

* added wandConfig to all timestamp subblocks on the platform

* added current time to timestamp wand generation

* fix file upload subblock styling, tested all fireflies ops

* removed dropdown for trigger for fireflies

* updated docs

* added fireflies to formatWebhookInput

* added more wandConfigs
2026-01-07 13:40:36 -08:00
Vikhyath Mondreti
3b4f7d6adb improvement(add-block): intuitive autoconnect + positioning (#2714)
* improvement(add-block): intuitive autoconnect + positioning

* cleanup code
2026-01-07 12:52:12 -08:00
Waleed
142c9a0428 fix(grain): add grain key to idempotency service (#2712)
* fix(grain): add grain key to idempotency service

* fixed dropdown issue for grain, webhook registration
2026-01-07 12:00:32 -08:00
Waleed
9dc02f3728 improvement(helm): added missing optional envvars to helm for whitelabeling (#2711) 2026-01-07 10:56:13 -08:00
Vikhyath Mondreti
833825f04a fix(deploy-check): race condition fixes (#2710) 2026-01-07 10:48:54 -08:00
Waleed
261becd129 feat(blacklist): added ability to blacklist models & providers (#2709)
* feat(blacklist): added ability to blacklist models & providers

* ack PR comments
2026-01-07 10:41:57 -08:00
Waleed
3ecf7a15eb feat(seo): updated out-of-date site metadata, removed unused static assets, updated emails (#2708)
* feat(seo): updated out-of-date site metadata, removed unused static assets, updated emails

* more

* more

* remove unused social photos
2026-01-07 09:38:40 -08:00
Waleed
1420bfb73c fix(resolver): add both new and old workflow blocks for backwards compatibility 2026-01-07 08:03:36 -08:00
Waleed
f5ab7f21ae v0.5.53: hotkey improvements, added redis fallback, fixes for workflow tool 2026-01-06 23:34:52 -08:00
Waleed
02229f0cb2 fix(agent-tool): fix workflow tool in agent to respect user-provided params, added badge for deployment status (#2705)
* fix(agent-tool): fix workflow tool in agent to respect user-provided params, added badge for deployment status

* ack PR comment

* updated gh stars
2026-01-06 23:22:59 -08:00
Waleed
a2451ef3d3 feat(locks): add no-op for locking without redis to allow deployments without redis (#2703)
* feat(locks): add no-op for locking without redis to allow deployments without redis

* ack PR comments, fixed worklfow block color
2026-01-06 23:14:34 -08:00
Waleed
6a262f3988 feat(i18n): update translations (#2702)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2026-01-06 19:57:29 -08:00
Waleed
5145ce1684 improvement(response): removed nested response block output, add docs for webhook block, styling improvements for subblocks (#2700)
* improvement(response): removed nested response block output, add docs for webhook block, styling improvements for subblocks

* remove outdated block docs

* updated docs

* remove outdated tests
2026-01-06 19:43:25 -08:00
Waleed
e5bd5e4474 fix(canvas): add handler for focus loss for hotkey operations (#2701) 2026-01-06 19:36:52 -08:00
Waleed
e9aede087d improvement(triggers): moved save configuration above instructions for better visibility, fixed styling inconsistencies (#2699) 2026-01-06 17:29:42 -08:00
Waleed
bfb6fffe38 v0.5.52: new port-based router block, combobox expression and variable support 2026-01-06 16:14:10 -08:00
Waleed
ba2377f83b feat(combobox): added expression support to combobox (#2697)
* feat(combobox): added expression support to combobox

* fix chat messages styling in light mode

* last sec stuff

* ack comments
2026-01-06 16:01:32 -08:00
Siddharth Ganesan
f502f984f3 improvement(router): add ports to router block (#2683)
* Add ports to router block

* Add tag dropdowns

* Fix lint

* fix tests + add context into block preview

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-06 15:44:25 -08:00
Waleed
74f371cc79 fix(build): fix type assertion (#2696)
* fix(build): fix type assertion

* ack PR comment

* more
2026-01-06 15:01:55 -08:00
Waleed
4fbec0a43f v0.5.51: triggers, kb, condition block improvements, supabase and grain integration updates 2026-01-06 14:26:46 -08:00
Waleed
d248557042 fix(settings): added isHosted gate to access homepage from settings, fixed context menu options (#2694)
* fix(settings): added isHosted gate to access homepage from settings, fixed context menu options

* stronger typing
2026-01-06 14:22:48 -08:00
Siddharth Ganesan
8215a819e5 improvement(hitl): add webhook notification and resume, add webhook block (#2673)
* Add api blcok as tool

* Add webhook block

* Hitl v1

* Cleanup

* Fix

* Update names for fields in hitl

* Fix hitl tag dropdown

* Update hitl dashboard

* Lint
2026-01-06 13:58:44 -08:00
Waleed
155f544ce8 feat(terminal): added terminal context menu (#2692) 2026-01-06 13:57:04 -08:00
Waleed
22f949a41c fix(condition): added success check on condition block processor, fixed terminal preventDefault copy bug (#2691) 2026-01-06 12:52:40 -08:00
Waleed
f9aef6ae22 feat(i18n): update translations (#2690)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2026-01-06 12:42:19 -08:00
Waleed
46b04a964d feat(supabase): added ability so select certain rows in supabase tools (#2689)
* feat(supabase): added ability so select certain rows in supabase tools

* ack PR comments
2026-01-06 12:21:08 -08:00
Waleed
964b40de45 fix(traces): remove child trace spans from workflow block after being merged with parent output (#2688) 2026-01-05 20:34:45 -08:00
Waleed
75aca00b6e improvement(kb): optimize processes, add more robust fallbacks for large file ops (#2684)
* improvement(kb): optimize processes, add more robust fallbacks for large file ops

* stronger typing

* comments cleanup

* ack PR comments

* upgraded turborepo

* ack more PR comments

* fix failing test

* moved doc update inside tx for embeddings chunks upload

* ack more PR comments
2026-01-05 20:26:16 -08:00
Vikhyath Mondreti
d25084e05d fix(child-workflow): hosted api key resolution (#2687) 2026-01-05 17:41:58 -08:00
Adam Gough
445932c1c8 fix(grain): fixed output and dropdown (#2685)
* fixed output and dropdown

* changed payload

* removed comments
2026-01-05 17:05:45 -08:00
Waleed
cc3f565d5e fix(webhook): strip extraneous fields from trigger processing (#2686) 2026-01-05 16:19:49 -08:00
Waleed
585f5e365b v0.5.50: import improvements, ui upgrades, kb styling and performance improvements 2026-01-05 00:35:55 -08:00
Waleed
0977ed228f improvement(kb): add configurable concurrency to chunks processing, sped up 22x for large docs (#2681) 2026-01-05 00:29:31 -08:00
Waleed
ed6b9c0c4a fix(kb): fix styling inconsistencies, add rename capability for documents, added search preview (#2680) 2026-01-04 23:47:54 -08:00
Adam Gough
86bcdcf0d3 fix(grain): save before deploying workflow (#2678)
* save before deployment fix

* moved to helper

* removed comment
2026-01-04 12:41:33 -08:00
Vikhyath Mondreti
ac942416de fix(kalshi): remove synthetically constructed outputs (#2677)
* fix(kalshi): remove synthetically constructed outputs

* fix api interface
2026-01-03 17:40:55 -08:00
Emir Karabeg
195e0e8e3f feat(popover): sections; improvement: tooltip, popover; fix(notifications): loading content (#2676) 2026-01-03 16:51:24 -08:00
Siddharth Ganesan
1673ef98ac fix(variables): fix variables block parsing error for json (#2675) 2026-01-03 14:42:39 -08:00
Siddharth Ganesan
356b473dc3 fix(import): fix missing blocks in import if undefined keys exist (#2674) 2026-01-03 14:37:59 -08:00
Waleed
3792bdd252 v0.5.49: hitl improvements, new email styles, imap trigger, logs context menu (#2672)
* feat(logs-context-menu): consolidated logs utils and types, added logs record context menu (#2659)

* feat(email): welcome email; improvement(emails): ui/ux (#2658)

* feat(email): welcome email; improvement(emails): ui/ux

* improvement(emails): links, accounts, preview

* refactor(emails): file structure and wrapper components

* added envvar for personal emails sent, added isHosted gate

* fixed failing tests, added env mock

* fix: removed comment

---------

Co-authored-by: waleed <walif6@gmail.com>

* fix(logging): hitl + trigger dev crash protection (#2664)

* hitl gaps

* deal with trigger worker crashes

* cleanup import strcuture

* feat(imap): added support for imap trigger (#2663)

* feat(tools): added support for imap trigger

* feat(imap): added parity, tested

* ack PR comments

* final cleanup

* feat(i18n): update translations (#2665)

Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>

* fix(grain): updated grain trigger to auto-establish trigger (#2666)

Co-authored-by: aadamgough <adam@sim.ai>

* feat(admin): routes to manage deployments (#2667)

* feat(admin): routes to manage deployments

* fix naming fo deployed by

* feat(time-picker): added timepicker emcn component, added to playground, added searchable prop for dropdown, added more timezones for schedule, updated license and notice date (#2668)

* feat(time-picker): added timepicker emcn component, added to playground, added searchable prop for dropdown, added more timezones for schedule, updated license and notice date

* removed unused params, cleaned up redundant utils

* improvement(invite): aligned styling (#2669)

* improvement(invite): aligned with rest of app

* fix(invite): error handling

* fix: addressed comments

---------

Co-authored-by: Emir Karabeg <78010029+emir-karabeg@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
Co-authored-by: Adam Gough <77861281+aadamgough@users.noreply.github.com>
Co-authored-by: aadamgough <adam@sim.ai>
2026-01-03 13:19:18 -08:00
Emir Karabeg
8d15219c12 improvement(invite): aligned styling (#2669)
* improvement(invite): aligned with rest of app

* fix(invite): error handling

* fix: addressed comments
2026-01-02 19:45:10 -08:00
Waleed
c3adcf315b feat(time-picker): added timepicker emcn component, added to playground, added searchable prop for dropdown, added more timezones for schedule, updated license and notice date (#2668)
* feat(time-picker): added timepicker emcn component, added to playground, added searchable prop for dropdown, added more timezones for schedule, updated license and notice date

* removed unused params, cleaned up redundant utils
2026-01-02 18:46:39 -08:00
Vikhyath Mondreti
4df5d56ac5 feat(admin): routes to manage deployments (#2667)
* feat(admin): routes to manage deployments

* fix naming fo deployed by
2026-01-02 17:58:19 -08:00
Adam Gough
7515809df0 fix(grain): updated grain trigger to auto-establish trigger (#2666)
Co-authored-by: aadamgough <adam@sim.ai>
2026-01-02 17:56:06 -08:00
Waleed
385e93f4bb feat(i18n): update translations (#2665)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2026-01-02 15:40:15 -08:00
Waleed
096af4fdfa feat(imap): added support for imap trigger (#2663)
* feat(tools): added support for imap trigger

* feat(imap): added parity, tested

* ack PR comments

* final cleanup
2026-01-02 15:28:00 -08:00
Vikhyath Mondreti
dc3de95c39 fix(logging): hitl + trigger dev crash protection (#2664)
* hitl gaps

* deal with trigger worker crashes

* cleanup import strcuture
2026-01-02 14:01:01 -08:00
Emir Karabeg
79be435918 feat(email): welcome email; improvement(emails): ui/ux (#2658)
* feat(email): welcome email; improvement(emails): ui/ux

* improvement(emails): links, accounts, preview

* refactor(emails): file structure and wrapper components

* added envvar for personal emails sent, added isHosted gate

* fixed failing tests, added env mock

* fix: removed comment

---------

Co-authored-by: waleed <walif6@gmail.com>
2026-01-02 00:16:27 -08:00
Waleed
852562cfdd feat(logs-context-menu): consolidated logs utils and types, added logs record context menu (#2659) 2026-01-01 13:47:30 -08:00
Waleed
eb5d1f3e5b v0.5.48: copy-paste workflow blocks, docs updates, mcp tool fixes 2025-12-31 18:00:04 -08:00
Waleed
4da128d77c improvement(context-menu): gray out undo redo if the stack is empty (#2657) 2025-12-31 17:59:44 -08:00
Waleed
0c8d05fc98 feat(workflow): added context menu for block, pane, and multi-block selection on canvas (#2656)
* feat(workflow): added context menu for block, pane, and multi-block selection on canvas

* added more

* ack PR comments
2025-12-31 14:42:33 -08:00
Vikhyath Mondreti
4301342ffb fix(mcp): exclude serverUrl from mcp tool call params (#2654) 2025-12-31 11:44:21 -08:00
Waleed
56e485d13b feat(i18n): update translations 2025-12-31 06:48:03 -08:00
Vikhyath Mondreti
1ed746bacf fix(paste): single instance trigger notification correction (#2653) 2025-12-31 03:34:14 -08:00
Vikhyath Mondreti
bf5d0a5573 feat(copy-paste): allow cross workflow selection, paste, move for blocks (#2649)
* feat(copy-paste): allow cross workflow selection, paste, move for blocks

* fix drag options

* add keyboard and mouse controls into docs

* refactor sockets and undo/redo for batch additions and removals

* fix tests

* cleanup more code

* fix perms issue

* fix subflow copy/paste

* remove log file

* fit paste in viewport bounds

* fix deselection
2025-12-31 02:47:06 -08:00
Adam Gough
fb148c6203 fix(jsm): renamed operation (#2651)
* renamed operaiton

* revert icons file
2025-12-31 00:08:20 -08:00
Waleed
54ab82c8dd v0.5.47: deploy workflow as mcp, kb chunks tokenizer, UI improvements, jira service management tools 2025-12-30 23:18:58 -08:00
Waleed
b90cc5b874 fix(build): resolve failing build due to symlink issue in main app dockerfile (#2650) 2025-12-30 22:48:55 -08:00
Waleed
4787909851 improvement(kb): removed zustand cache syncing in kb, added chunk text tokenizer (#2647)
* improvement(kb): removed zustand cache syncing in kb, added chunk text tokenizer

* removed dead code

* removed redundant hook

* remove unused hook

* remove alert notification and use simple error

* added more popover actions

* removed debug instrumentation

* remove extraneous comments

* removed unused handler
2025-12-30 20:15:08 -08:00
Waleed
776f82c0a6 feat(i18n): update translations (#2645)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2025-12-30 19:50:11 -08:00
Vikhyath Mondreti
2cfd75a422 fix(tool-input): code subblock should be emptyable (#2646) 2025-12-30 17:45:56 -08:00
Priyanshu Solanki
c77268c13d feat(workflow-as-mcp): added ability to deploy workflows as mcp servers and mcp tools (#2415)
* added a workflow as mcp

* fixed the issue of UI rendering for deleted mcp servers

* fixing lint issues

* using mcn components

* fixing merge conflicts

* fix

* fix lint errors

* refactored code to use hasstartblock from the tirgger utils

* removing unecessary auth

* using official mcp sdk and added description fields

* using normalised input schema function

* ui fixes part 1

* remove migration before merge

* fix merge conflicts

* remove migration to prep merge

* re-add migration

* cleanup code to use mcp sdk types

* fix discovery calls

* add migration

* ui improvements

* fix lint

* fix types

* fix lint

* fix spacing

* remove migration to prep merge

* add migration back

* fix imports

* fix tool refresh ux

* fix test failures

* fix tests

* cleanup code

* styling improvements, ability to edit mcp server description, etc

* fixed ui in light mode api keys modal

* update docs

* deprecated unused input components, shifted to emcn

* updated playground, simplified components

* move images and videos

* updated more docs images

---------

Co-authored-by: priyanshu.solanki <priyanshu.solanki@saviynt.com>
Co-authored-by: Siddharth Ganesan <siddharthganesan@gmail.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: waleed <walif6@gmail.com>
2025-12-30 16:52:50 -08:00
Waleed
34bc115468 feat(i18n): update translations (#2643)
Co-authored-by: icecrasher321 <icecrasher321@users.noreply.github.com>
2025-12-30 16:27:37 -08:00
Adam Gough
2697da5d9c fix(jsm): combined jira providers for jsm (#2644) 2025-12-30 16:01:35 -08:00
Waleed
eca91232bf improvement(tools): added input validation to jira service management routes (#2642) 2025-12-30 14:55:08 -08:00
Adam Gough
7356edccbb added jsm (#2641)
Co-authored-by: aadamgough <adam@sim.ai>
2025-12-30 14:46:31 -08:00
Waleed
9208375523 feat(cursorrules): updated cursorrules and claude md file (#2640)
* feat(cursorrules): updated cursorrules and claude md file

* added rules for adding new integrations
2025-12-30 10:58:33 -08:00
Waleed
df099e9485 feat(filtering): added the ability to filter logs by date and date range (#2639) 2025-12-30 10:42:44 -08:00
ppippi-dev
f8b1880575 fix(docker): resolve @sim/logger module not found in realtime container (#2637) 2025-12-29 23:06:28 -08:00
Emir Karabeg
a7a7c8601c improvement(copilot): ui/ux; refactor: store dimensions (#2636) 2025-12-29 20:42:42 -08:00
Waleed
97a9295230 feat(kb): added permissions to workspace popover, added kb popover to view tags, edit description and kb name (#2634) 2025-12-29 18:56:50 -08:00
Emir Karabeg
e9e5721610 improvement: HITL, subblocks, general (#2633)
* improvement: knowledge tags subblock, start subblock

* improvement: terminal height, subblocks

* improvement: general settings, HITL
2025-12-29 17:51:07 -08:00
Emir Karabeg
a08d86d42b fix(note): light mode (#2631) 2025-12-29 15:39:47 -08:00
Vikhyath Mondreti
400178a3b0 fix(templates): only change updatedAt for actual updates to workflow or metadata (#2630) 2025-12-29 12:11:32 -08:00
Waleed
da1f668272 improvement(sidebar): fix workspace name truncation on sidebar preview (#2628) 2025-12-29 11:11:00 -08:00
Waleed
f895bf469b v0.5.46: build improvements, greptile, light mode improvements 2025-12-29 02:17:52 -08:00
Waleed
88065088bf fix(deploy): fix workflow change detection to handle old variable reference format (#2623) 2025-12-29 02:09:38 -08:00
Emir Karabeg
1c626dfcae improvement(globals): light colors (#2620)
* improvement(globals): light colors

* improvement(ui): logs, templates, kb, light globals

* improvement(subflows): start node and ui/ux
2025-12-28 13:28:36 -08:00
Waleed
132aae1615 feat(i18n): update translations (#2619)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2025-12-28 12:37:20 -08:00
Waleed
f44fc18041 fix(build): add tsconfig to db dockerfile (#2617)
* fix(build): add tsconfig to db dockerfile

* ack pr comment
2025-12-28 12:29:34 -08:00
Waleed
7761b16b87 feat(tools): added greptile tools/block, updated copilot panel styling (#2618) 2025-12-28 12:12:22 -08:00
Waleed
71130c8b0a improvement(monorepo): added tsconfig package, resolved type errors in testing package (#2613) 2025-12-28 00:36:48 -08:00
Waleed
dd3209af06 v0.5.45: light mode fixes, realtime usage indicator, docker build improvements 2025-12-27 19:57:42 -08:00
Waleed
7c0a3c15ac improvement(build): migrate to blacksmith sticky disks for faster builds, other build improvements (#2611) 2025-12-27 18:09:29 -08:00
Waleed
cdc1a832d7 fix(docker): add logger package to realtime dockerfile (#2610) 2025-12-27 17:55:16 -08:00
Waleed
aa9cc5604a improvement(usage-indicator): update query invalidation for usage to update in realtime (#2607)
* improvement(usage-indicator): update query invalidation for usage to update in realtime

* ack PR comments
2025-12-27 15:23:02 -08:00
Emir Karabeg
fdba1cfac2 improvement: required permissions, oauth modal badge (#2609) 2025-12-27 15:18:03 -08:00
Waleed
2e1ccb16f5 improvement(ui): hide divider when following subblock value is null (#2608)
* improvement(ui): hide divider when following subblock value is null

* closed gap in mcp dynamic args subblock
2025-12-27 15:15:43 -08:00
Waleed
b6ba3b50a7 v0.5.44: keyboard shortcuts, autolayout, light mode, byok, testing improvements 2025-12-26 21:25:19 -08:00
Waleed
8651896277 feat(i18n): update translations (#2604)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2025-12-26 21:11:01 -08:00
Emir Karabeg
3054d6c1ed fix: bg styling outside workspace (#2605) 2025-12-26 21:08:13 -08:00
Waleed
3d75445459 feat(tools): added new firecrawl agent endpoint (#2603) 2025-12-26 20:34:39 -08:00
Waleed
5add2613ff improvement(easyconnect): use native reactflow getIntersectingNodes instead of custom impl for easy connect (#2601) 2025-12-26 18:29:03 -08:00
Waleed
bd0eca04d7 fix(tag-dropdown): fix the way variables are displayed in the tag dropdown (#2597) 2025-12-26 17:17:01 -08:00
Adam Gough
a60a1fc49a improvement(edges): drag edge over block (#2596)
* drag edge over block

* fix minor stale closure

---------

Co-authored-by: aadamgough <adam@sim.ai>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2025-12-26 17:05:15 -08:00
Vikhyath Mondreti
298546daf1 fix(parallel): add parallel sentinel to make parallel-parallel and parallel-loop work correctly (#2593)
* fix(parallel): add parallel sentinel to make parallel-parallel and parallel-loop work correctly

* fix regular -> parallel + copilot nested subflows

* add tests

* consolidate to always explode parallel dag at runtime
2025-12-26 16:51:54 -08:00
Waleed
b60b98e42c improvement(tag-dropdown): added option to select block in tag dropdown, custom tools modal improvements, light mode fixes (#2594)
* improvement(tag-dropdown): added option to select block in tag dropdown, custom tools modal improvements, light mode fixes

* fix UI bugs

* remove unused components

* tag drop ordering fix
2025-12-26 16:24:38 -08:00
Waleed
7793a6d597 feat(kb): added tags information to kb docs table (#2589)
* feat(kb): added tags information to kb docs table

* improvement(base): table and tags styling

---------

Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
2025-12-26 13:54:33 -08:00
Vikhyath Mondreti
66b8434861 fix(block-name): updating block name should update downstream var refs (#2592)
* fix(block-name): updating block name should update downstream var refs

* remove random comments
2025-12-26 12:56:02 -08:00
Adam Gough
27ec4120bc fix(tools): fixed tool outputs (#2534)
* fixed typeform

* fixed supabase tools

* salesforce fix

* removed comments

* pinecone fix

* fixed intercom

* regex constant

* removed fallback logic
2025-12-26 12:46:39 -08:00
Emir Karabeg
1f0e3f2be6 feat: light mode (#2457)
* feat(light): restored light theme

* feat: styling consolidation, note block upgrades

* improvement(zoom-prevention): moved downstream

* fix(notifications): mismatching workflow ID

* feat(light): button variant updates and controls consolidation

* improvement: UI consolidation

* feat: badges, usage limit; fix(note): iframe security; improvement(s-modal): sizing

* improvement: oauth modal, subscription

* improvement(team): ui/ux

* feat: emcn, subscription, tool input

* improvement(copilot): styling consolidation

* feat: colors consolidation

* improvement(ui): light styling

* fix(build): unused billing component

* improvement: addressed comments
2025-12-26 12:45:06 -08:00
Waleed
88cda3a9ce improvement(billing): migrate to decimaljs from number.parseFloat (#2588)
* improvement(billing): migrate to decimaljs from number.parseFloat

* ack PR comments

* ack pr comment

* consistency
2025-12-26 12:35:49 -08:00
Waleed
d707d18ee6 fix(build): update dockerfile to contain testing package deps (#2591)
* fix(build): update dockerfile to contain testing package deps

* added logger package
2025-12-26 12:20:38 -08:00
Waleed
b7f6bab282 feat(tests): added testing package, overhauled tests (#2586)
* feat(tests): added testing package, overhauled tests

* fix build
2025-12-25 16:06:47 -08:00
Waleed
61e7213425 feat(i18n): update translations (#2585)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2025-12-25 13:33:14 -08:00
Waleed
3201abab56 improvement(schedules): use tanstack query to fetch schedule data, cleanup ui on schedule info component (#2584)
* improvement(schedules): use tanstack query to fetch schedule data, cleanup ui on schedule info component

* update trigger-save UI, increase auto disable to 100 consecutive from 10

* updated docs

* consolidate consts
2025-12-25 12:09:58 -08:00
Waleed
d79696beae feat(docs): added vector search (#2583)
* feat(docs): added vector search

* ack comments
2025-12-25 11:00:57 -08:00
Waleed
f604ca39a5 feat(chat-otp): added db fallback for chat otp (#2582)
* feat(chat-otp): added db fallback for chat otp

* ack PR comments
2025-12-25 09:37:20 -08:00
Waleed
26ec12599f improvement(byok): updated styling for byok page (#2581) 2025-12-25 08:36:55 -08:00
Waleed
97372533ec feat(i18n): update translations (#2578) 2025-12-24 23:37:35 -08:00
Vikhyath Mondreti
66766a9d81 improvement(byok): remove web search block exa (#2579)
* remove exa from byok

* improvement(byok): remove web search block exa

* fix autolayout

* fix type
2025-12-24 19:26:48 -08:00
Vikhyath Mondreti
47a259b428 feat(byok): byok for hosted model capabilities (#2574)
* feat(byok): byok for hosted model capabilities

* fix type

* add ignore lint

* accidentally added feature flags

* centralize byok fetch for LLM calls

* remove feature flags ts

* fix tests

* update docs
2025-12-24 18:20:54 -08:00
Waleed
40a6bf5c8c improvement(variables): update workflows to use deployed variables, not local ones to align with the rest of the canvas components (#2577)
* improvement(variables): update workflows to use deployed variables, not local ones to align with the rest of the canvas components

* update change detection to ignore trigger id since it is runtime metadata and not actually required to be redeployed
2025-12-24 17:40:23 -08:00
Waleed
da7eca9590 fix(change-detection): move change detection logic to client-side to prevent unnecessary API calls, consolidate utils (#2576)
* fix(change-detection): move change detection logic to client-side to prevent unnecessary API calls, consolidate utils

* added tests

* ack PR comments

* added isPublished to API response
2025-12-24 17:16:35 -08:00
Waleed
92b2e34d25 feat(autolayout): add fitToView on autolayout and reduce horizontal spacing between blocks (#2575)
* feat(autolayout): add fitToView on autolayout and reduce horizontal spacing between blocks

* remove additional yaml code
2025-12-24 16:19:29 -08:00
Vikhyath Mondreti
77521a3a57 fix(cancel-workflow-exec): move cancellation tracking for multi-task envs to redis (#2573)
* fix(cancel-workflow-exec): move cancellation tracking for multi-task envs to redis

* cleanup cancellation keys after execution
2025-12-24 11:51:09 -08:00
Waleed
cb8b9c547a fix(router): update router to handle azure creds the same way the agent block does (#2572)
* fix(router): update router to handle azure creds the same way the agent block does

* cleanup
2025-12-24 10:22:47 -08:00
Vikhyath Mondreti
b1cd8d151d fix(executor): workflow abort has to send abort signal to route for correct state update (#2571) 2025-12-24 02:50:58 -08:00
Waleed
1145f5c043 fix(shortcut): fixed global keyboard commands provider to follow latest ref pattern (#2569)
* fix(shortcut): fixed global commands provider to follow best practices

* cleanup

* ack PR comment
2025-12-24 00:25:15 -08:00
Waleed
b304233062 v0.5.43: export logs, circleback, grain, vertex, code hygiene, schedule improvements 2025-12-23 19:19:18 -08:00
Waleed
3a50ce4d99 feat(i18n): update translations (#2568)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2025-12-23 19:03:43 -08:00
Waleed
810d2089cf feat(schedules): remove save button for schedules, couple schedule deployment with workflow deployment (#2566)
* feat(schedules): remove save button for schedules, couple schedule deployment with workflow deployment

* added tests

* ack PR comments

* update turborepo

* cleanup, edge cases

* ack PR comment
2025-12-23 18:53:40 -08:00
Vikhyath Mondreti
8c89507247 improvement(logs): state machine of workflow execution (#2560)
* improvement(logs): state machine of workflow execution

* cleanup more code

* fallback consistency

* fix labels

* backfill in migration correctly

* make streaming stop in chat window correctly
2025-12-23 18:27:19 -08:00
Vikhyath Mondreti
169dd4a503 fix(grafana): tool outputs (#2565)
* fix(grafana): list annotations outputs

* fix more grafana tools
2025-12-23 17:23:12 -08:00
Vikhyath Mondreti
dc4e5d3bdc fix(dropbox): access type param pass through to get refresh token (#2564) 2025-12-23 16:32:58 -08:00
Emir Karabeg
31de55cbdf feat(ux): add expandFolder to auto expand folders on nested folder creation (#2562)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-12-23 16:27:00 -08:00
Waleed
eaca49037d fix(ui): remove css transition on popover and dropdown items to avoid flicker (#2563) 2025-12-23 15:46:27 -08:00
Waleed
2d26c0cb32 feat(i18n): update translations (#2561)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2025-12-23 15:42:54 -08:00
Waleed
cdf3d759b9 fix(jina): removed conditionally included outputs from jina (#2559)
* fix(jina): removed conditionally included outputs from jina

* ack PR comments
2025-12-23 15:20:10 -08:00
Vikhyath Mondreti
bf8fbebe22 improvement(code-quality): centralize regex checks, normalization (#2554)
* improvement(code-quality): centralize regex checks, normalization

* simplify resolution

* fix(copilot): don't allow duplicate name blocks

* centralize uuid check
2025-12-23 15:12:04 -08:00
Waleed
b23299dae4 feat(i18n): update translations (#2558)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2025-12-23 14:54:44 -08:00
Waleed
6c8f1a81c1 feat(tools): added grain and circleback (#2557)
* feat(tools): added grain and circleback

* oauth to pat and tool fix

* updated docs

* remove remaining oauth references

* cleanup

---------

Co-authored-by: aadamgough <adam@sim.ai>
2025-12-23 14:34:12 -08:00
Waleed
2c36926a4e fix(perplexity): remove deprecated perplexity sonar reasoning model (#2556) 2025-12-23 13:24:18 -08:00
Waleed
89c1085950 improvement(vertex): added vertex to all LLM-based blocks, fixed refresh (#2555)
* improvement(vertex): added vertex to all LLM-based blocks, fixed refresh

* fix build
2025-12-23 13:11:56 -08:00
Waleed
4e09c389e8 improvement(usage): update usage limit in realtime, standardize token output object across providers (#2553)
* improvement(usage-limit): update usage in real time, fix token output object

* updated tokenBreakdown to tokens, standardized input/output/total token object type across providers

* update remaining references

* ack PR comment

* remove singleton query client instance from hooks, leave only in zustand
2025-12-23 13:04:47 -08:00
Vikhyath Mondreti
641ac58017 fix(frozen-canvas): need to fetch the deployment version correctly (#2552) 2025-12-23 11:37:07 -08:00
Waleed
6c1e4ff7d6 improvement(oauth): remove unused scope hints (#2551)
* improvement(oauth): remove unused scope hints

* improvement(oauth): remove scopeHints and extraneous oauth provider data

* cleanup
2025-12-23 11:26:49 -08:00
Waleed
40e30a11e9 improvement(logs): update logs export route to respect filters (#2550) 2025-12-23 10:23:27 -08:00
Vikhyath Mondreti
57e4b49bd6 v0.5.42: fix memory migration 2025-12-23 01:24:54 -08:00
Vikhyath Mondreti
d1ebad912e fix memory migration (#2548) 2025-12-23 01:19:29 -08:00
Vikhyath Mondreti
e12dd204ed v0.5.41: memory fixes, copilot improvements, knowledgebase improvements, LLM providers standardization 2025-12-23 00:15:18 -08:00
Vikhyath Mondreti
621f9a40c7 improvement(landing): free usage limit (#2547) 2025-12-23 00:07:13 -08:00
Siddharth Ganesan
3100daa346 feat(copilot): add tools to access block outputs and upstream references (#2546)
* Add copilot references tools

* Minor fixes

* Omit vars field in block outputs when id is provided
2025-12-23 00:06:24 -08:00
Priyanshu Solanki
c252e885af improvement(logs): fixed logs for parallel and loop execution flow (#2468)
* fixed logs for parallel and loop execution flow

* Fix array check for collection

* fixed for empty loop and paralle blocks and showing input on dashboard

* extracted utility functions

* fixed the refrencing errors and making sure it propogates to the console

* fix parallel

* fix tests'

---------

Co-authored-by: priyanshu.solanki <priyanshu.solanki@saviynt.com>
Co-authored-by: Siddharth Ganesan <siddharthganesan@gmail.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2025-12-23 00:02:02 -08:00
Waleed
b0748c82f9 fix(search): removed full text param from built-in search, anthropic provider streaming fix (#2542)
* fix(search): removed full text param from built-in search, anthropic provider streaming fix

* rewrite gemini provider with official sdk + add thinking capability

* vertex gemini consolidation

* never silently use different model

* pass oauth client through the googleAuthOptions param directly

* make server side provider registry

* remove comments

* take oauth selector below model selector

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2025-12-22 23:57:11 -08:00
Waleed
f5245f3eca fix(billing): add line items for wand (#2543)
* fix(billing): add line items for wand

* ack pr comment
2025-12-22 23:06:14 -08:00
Waleed
f2ef5f0811 feat(i18n): update translations (#2541)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2025-12-22 21:17:01 -08:00
Waleed
37443a7b77 improvement(kb): improve chunkers, respect user-specified chunk configurations, added tests (#2539)
* improvement(kb): improve chunkers, respect user-specified chunk configurations, added tests

* ack PR commnets

* updated docs

* cleanup
2025-12-22 20:47:29 -08:00
lif
e0d96e2126 fix(oauth): add User-Agent header to Reddit token refresh (#2517)
Reddit API requires User-Agent header for all requests including OAuth
token refresh. Without it, requests fail with 403 error after the
initial token expires.

Fixes #1822
2025-12-22 20:09:38 -08:00
Waleed
827dd0466f feat(i18n): update translations (#2538)
Co-authored-by: icecrasher321 <icecrasher321@users.noreply.github.com>
2025-12-22 20:09:03 -08:00
Vikhyath Mondreti
ccd92b9054 improvement(block-metadata): remove references to yaml syntax in best practices (#2537) 2025-12-22 20:07:29 -08:00
Vikhyath Mondreti
6af291ca9f improvement(pricing): increase free user limit to 20 usd (#2536)
* improvement(pricing): increase free user limit to 20 usd

* make gemini pricing accurate

* generate migration for db constant

* update docs

* test notif data
2025-12-22 19:51:22 -08:00
Vikhyath Mondreti
be9ab4c833 fix(dashboard): flash based on loading check (#2535) 2025-12-22 18:49:41 -08:00
Emir Karabeg
ab3a3d12fe improvement(logs): dashboard/logs optimizations and improvements (#2414)
* improvement(logs): dashboard/logs optimizations and improvements

* improvement: addressed comments

* improvement: loading

* cleanup

* ack PR comments

* cleanup more

---------

Co-authored-by: waleed <walif6@gmail.com>
2025-12-22 16:44:10 -08:00
Waleed
e01d4cb990 feat(i18n): update translations (#2530)
Co-authored-by: icecrasher321 <icecrasher321@users.noreply.github.com>
2025-12-22 16:11:59 -08:00
Vikhyath Mondreti
8c2c49eb14 fix(models): memory fixes, provider code typing, cost calculation cleanup (#2515)
* improvement(memory): should not be block scoped

* cleanup provider code

* update other providers

* cleanup fallback code

* remove flaky test

* fix memory

* move streaming fix to right level

* cleanup streaming server

* make memories workspace scoped

* update docs

* fix dedup logic

* fix streaming parsing issue for multiple onStream calls for same block

* fix(provieders): support parallel agent tool calls, consolidate utils

* address greptile comments

* remove all comments

* fixed openrouter response format handling, groq & cerebras response formats

* removed duplicate type

---------

Co-authored-by: waleed <walif6@gmail.com>
2025-12-22 15:59:53 -08:00
Waleed
086982c7a3 improvement(docs): update og image (#2529) 2025-12-22 13:16:39 -08:00
Waleed
2b7807a7de feat(i18n): update translations (#2526)
* feat(i18n): update translations

* fixed build

---------

Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2025-12-22 11:20:42 -08:00
Waleed
2d4a660246 feat(intercom): added additional params to intercom tools (#2523) 2025-12-22 10:24:49 -08:00
Siddharth Ganesan
e981b1dc1b improvement(copilot): improve copilot metadata processing and tool output memory (#2516) 2025-12-21 23:24:55 -08:00
Vikhyath Mondreti
3d9d9cbc54 v0.5.40: supabase ops to allow non-public schemas, jira uuid 2025-12-21 22:28:05 -08:00
Adam Gough
58fcb4ed80 fix(jira): added uuid (#2513)
* added uuid param

* revmoed comment

* fixed naming
2025-12-21 22:04:05 -08:00
Vikhyath Mondreti
e4d211c2f0 improvement(supabase): allow non-public schemas (#2511) 2025-12-21 21:20:36 -08:00
Waleed
0f4ec962ad v0.5.39: notion, workflow variables fixes 2025-12-20 20:44:00 -08:00
Waleed
f21eaf1f10 fix(vars): add socket persistence when variable names are changed, update variable name normalization to match block name normalization, added space constraint on envvar names (#2508)
* fix(vars): add socket persistence when variable names are changed, update variable name normalization to match block name normalization, added space constraint on envvar names

* removed redundant queueing, removed unused immediate flag from sockets ops

* ack PR comments
2025-12-20 20:35:28 -08:00
Waleed
942da8815d fix(notion): remove hyphenation of incoming page ID's (#2507) 2025-12-20 19:35:44 -08:00
Waleed
4827866f9a v0.5.38: snap to grid, copilot ux improvements, billing line items 2025-12-20 17:24:38 -08:00
Waleed
214632604d feat(settings): added snap to grid slider to settings (#2504)
* feat(settings): added snap to grid slider to settings

* ack PR comments

* ack PR comment
2025-12-20 16:54:40 -08:00
Vikhyath Mondreti
1ddbac1d2e fix(code): cmd-z after refocus should not clear subblock (#2503) 2025-12-20 16:26:30 -08:00
Waleed
35a57bfad4 feat(audit): added audit log for billing line items (#2500)
* feat(audit): added audit log for billing line items

* remove migration

* reran migrations after resolving merge conflict

* ack PR comment
2025-12-20 14:10:01 -08:00
Waleed
f8678b179a fix(migrations): remove duplicate indexes (#2501) 2025-12-20 13:55:26 -08:00
Siddharth Ganesan
0ebb45b2db feat(copilot): show inline prompt to increase usage limit or upgrade plan (#2465)
* Add limit v1

* fix ui for copilot upgrade limit inline

* open settings modal

* Upgrade plan button

* Remove comments

* Ishosted check

* Fix hardcoded bumps

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2025-12-20 13:46:06 -08:00
Waleed
6247f421bc improvement(queries): add workspaceId to execution logs, added missing indexes based on query insights (#2471)
* improvement(queries): added missing indexes

* add workspaceId to execution logs

* remove migration to prep merge

* regen migration

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2025-12-20 13:33:10 -08:00
Waleed
3e697d9ed9 v0.5.37: redaction utils consolidation, logs updates, autoconnect improvements, additional kb tag types 2025-12-19 22:31:55 -08:00
Waleed
6385d82b85 improvement(ui): updated kb tag component to match existing table (#2498)
* improvement(ui): updated kb tag component to match existing table

* fix selection

* fix more ui

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2025-12-19 22:26:24 -08:00
Waleed
f91beb324e fix(condition): fixed deactivated edges when if and else if conditions connected to same destination block, added 100+ unit tests (#2497) 2025-12-19 21:12:49 -08:00
Priyanshu Solanki
4f69b171f2 feat(kb): Adding support for more tags to the KB (#2433)
* creating boolean, number and date tags with different equality matchings

* feat: add UI for tag field types with filter operators

- Update base-tags-modal with field type selector dropdown
- Update document-tags-modal with different input types per fieldType
- Update knowledge-tag-filters with operator dropdown and type-specific inputs
- Update search routes to support all tag slot types
- Update hook to use AllTagSlot type

* feat: add field type support to document-tag-entry component

- Add dropdown with all field types (Text, Number, Date, Boolean)
- Render different value inputs based on field type
- Update slot counting to include all field types (28 total)

* fix: resolve MAX_TAG_SLOTS error and z-index dropdown issue

- Replace MAX_TAG_SLOTS with totalSlots in document-tag-entry
- Add z-index to SelectContent in base-tags-modal for proper layering

* fix: handle non-text columns in getTagUsage query

- Only apply empty string check for text columns (tag1-tag7)
- Numeric/date/boolean columns only check IS NOT NULL
- Cast values to text for consistent output

* refactor: use EMCN components for KB UI

- Replace @/components/ui imports with @/components/emcn
- Use Combobox instead of Select for dropdowns
- Use EMCN Switch, Button, Input, Label components
- Remove unsupported 'size' prop from EMCN Button

* fix: layout for delete button next to date picker

- Change delete button from absolute to inline positioning
- Add proper column width (w-10) for delete button
- Add empty header cell for delete column
- Apply fix to both document-tag-entry and knowledge-tag-filters

* fix: clear value when switching tag field type

- Reset value to empty when changing type (e.g., boolean to text)
- Reset value when tag name changes and type differs
- Prevents 'true'/'false' from sticking in text inputs

* feat: add full support for number/date/boolean tag filtering in KB search

- Copy all tag types (number, date, boolean) from document to embedding records
- Update processDocumentTags to handle all field types with proper type conversion
- Add number/date/boolean columns to document queries in checkDocumentWriteAccess
- Update chunk creation to inherit all tag types from parent document
- Add getSearchResultFields helper for consistent query result selection
- Support structured filters with operators (eq, gt, lt, between, etc.)
- Fix search queries to include all 28 tag fields in results

* fixing tags import issue

* fix rm file

* reduced number to 3 and date to 2

* fixing lint

* fixed the prop size issue

* increased number from 3 to 5 and boolean from 7 to 2

* fixed number the sql stuff

* progress

* fix document tag and kb tag modals

* update datepicker emcn component

* fix ui

* progress on KB block tags UI

* fix issues with date filters

* fix execution parsing of types for KB tags

* remove migration before merge

* regen migrations

* fix tests and types

* address greptile comments

* fix more greptile comments

* fix filtering logic for multiple of same row

* fix tests

---------

Co-authored-by: priyanshu.solanki <priyanshu.solanki@saviynt.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2025-12-19 21:00:35 -08:00
Waleed
a1a189f328 fix(condition): remove dead code from condition handler, defer resolution to function execute tool like the function block (#2496) 2025-12-19 20:18:42 -08:00
Waleed
7dc48510dc fix(tool-input): allow multiple instances of workflow block or kb tools as agent tools (#2495)
* fix(tool-input): allow multiple instances of workflow block or kb tools as agent tools

* ack PR comments
2025-12-19 19:19:42 -08:00
Martin Yankov
4431a1a484 fix(helm): add custom egress rules to realtime network policy (#2481)
The realtime service network policy was missing the custom egress rules section
that allows configuration of additional egress rules via values.yaml. This caused
the realtime pods to be unable to connect to external databases (e.g., PostgreSQL
on port 5432) when using external database configurations.

The app network policy already had this section, but the realtime network policy
was missing it, creating an inconsistency and preventing the realtime service
from accessing external databases configured via networkPolicy.egress values.

This fix adds the same custom egress rules template section to the realtime
network policy, matching the app network policy behavior and allowing users to
configure database connectivity via values.yaml.
2025-12-19 18:59:08 -08:00
Waleed
93fe68785e fix(subflow): prevent auto-connect across subflow edges with keyboard shortcut block additions, make positioning for auto-drop smarter (#2489)
* fix(subflow): prevent auto-connect across subflow edges with keyboard shortcut block additions, make positioning for auto-drop smarter

* stronger typing
2025-12-19 18:31:29 -08:00
Vikhyath Mondreti
50c1c6775b fix(logs): always capture cost, logging size failures (#2487)
* fix(logs): truncate strings in tracespans crashing insertion

* add depth check to not crash

* custom serialization to not break tracepsans

* log costs even in log creation failure

* code cleanup?

* fix typing

* remove null bytes

* increase char limit

* reduce char limit
2025-12-19 17:39:18 -08:00
Waleed
df5f823d1c fix(autofill): add dummy inputs to prevent browser autofill for various fields, prevent having 0 workflows in workspace (#2482)
* fix(autofill): add dummy inputs to prevent browser autofill for various fields, prevent having 0 workflows in workspace

* cleanup

* ack PR comments

* fix failing test
2025-12-19 15:29:01 -08:00
Waleed
094f87fa1f fix(edges): prevent autoconnect outgoing edges from response block (#2479) 2025-12-19 13:19:53 -08:00
Waleed
65efa039da fix(redaction): consolidate redaction utils, apply them to inputs and outputs before persisting logs (#2478)
* fix(redaction): consolidate redaction utils, apply them to inputs and outputs before persisting logs

* added testing utils
2025-12-19 13:17:51 -08:00
Waleed
6b15a50311 improvement(ui): updated subscription and team settings modals to emcn (#2477) 2025-12-19 11:41:47 -08:00
Waleed
65787d7cc3 fix(api-keys): remove billed account check during api key generation (#2476) 2025-12-19 11:33:00 -08:00
Waleed
4d1a9a3f22 v0.5.36: hitl improvements, opengraph, slack fixes, one-click unsubscribe, auth checks, new db indexes 2025-12-19 01:27:49 -08:00
Waleed
656a6b8abd fix(sanitization): added more input sanitization to tool routes (#2475)
* fix(sanitization): added more input sanitization to tool routes

* ack PR comments
2025-12-19 01:27:20 -08:00
Waleed
889b44c90a improvement(db): added missing indexes for common access patterns (#2473) 2025-12-19 00:46:10 -08:00
Waleed
3a33ec929f fix(authentication): added auth checks for various routes, mysql and postgres query validation, csp improvements (#2472) 2025-12-19 00:44:52 -08:00
Waleed
24356d99ec fix(unsubscribe): add one-click unsubscribe (#2467)
* fix(unsubscribe): add one-click unsubscribe

* ack Pr comments
2025-12-18 21:16:24 -08:00
Waleed
6de1c04517 feat(i18n): update translations (#2470) 2025-12-18 21:01:51 -08:00
Adam Gough
38be2b76c4 fix(slack): respect message limit, remove duplicate canonical representations (#2469)
* fix(slack): respect message limit, remove duplicate canonical representations

* removed comment

* updated docs script

---------

Co-authored-by: aadamgough <adam@sim.ai>
2025-12-18 20:37:14 -08:00
Waleed
a2f14cab54 feat(og): add opengraph images for templates, blogs, and updated existing opengraph image for all other pages (#2466)
* feat(og): add opengraph images for templates, blogs, and updated existing opengraph image for all other pages

* added to workspace templates page as well

* ack PR comments
2025-12-18 19:15:06 -08:00
Priyanshu Solanki
474762d6fb improvement(hitl): show resume url in tag dropdown within hitl block (#2464)
* fixed the human in the loop url resolution:

* greptilecomments

* greptilecomments

---------

Co-authored-by: Pbonmars-20031006@users.noreply.github.com
2025-12-18 19:43:37 -07:00
Waleed
0005c3e465 feat(i18n): update translations (#2463)
Co-authored-by: aadamgough <aadamgough@users.noreply.github.com>
2025-12-18 18:18:31 -08:00
Adam Gough
fc40b4f7af fix(tools): improved slack output ux and jira params (#2462)
* fixed slack output

* updated jira

* removed comment

* change team uuid
2025-12-18 17:56:10 -08:00
Vikhyath Mondreti
eb07a080fb v0.5.35: helm updates, copilot improvements, 404 for docs, salesforce fixes, subflow resize clamping 2025-12-18 16:23:19 -08:00
6693 changed files with 1275864 additions and 159661 deletions

View File

@@ -0,0 +1,825 @@
---
description: Create a block configuration for a Sim integration with proper subBlocks, conditions, and tool wiring
argument-hint: <service-name>
---
# Add Block Skill
You are an expert at creating block configurations for Sim. You understand the serializer, subBlock types, conditions, dependsOn, modes, and all UI patterns.
## Your Task
When the user asks you to create a block:
1. Create the block file in `apps/sim/blocks/blocks/{service}.ts`
2. Configure all subBlocks with proper types, conditions, and dependencies
3. Wire up tools correctly
## Block Configuration Structure
```typescript
import { {ServiceName}Icon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import { getScopesForService } from '@/lib/oauth/utils'
export const {ServiceName}Block: BlockConfig = {
type: '{service}', // snake_case identifier
name: '{Service Name}', // Human readable
description: 'Brief description', // One sentence
longDescription: 'Detailed description for docs',
docsLink: 'https://docs.sim.ai/tools/{service}',
category: 'tools', // 'tools' | 'blocks' | 'triggers'
bgColor: '#HEXCOLOR', // Brand color
icon: {ServiceName}Icon,
// Auth mode
authMode: AuthMode.OAuth, // or AuthMode.ApiKey
subBlocks: [
// Define all UI fields here
],
tools: {
access: ['tool_id_1', 'tool_id_2'], // Array of tool IDs this block can use
config: {
tool: (params) => `{service}_${params.operation}`, // Tool selector function
params: (params) => ({
// Transform subBlock values to tool params
}),
},
},
inputs: {
// Optional: define expected inputs from other blocks
},
outputs: {
// Define outputs available to downstream blocks
},
}
```
## SubBlock Types Reference
**Critical:** Every subblock `id` must be unique within the block. Duplicate IDs cause conflicts even with different conditions.
### Text Inputs
```typescript
// Single-line input
{ id: 'field', title: 'Label', type: 'short-input', placeholder: '...' }
// Multi-line input
{ id: 'field', title: 'Label', type: 'long-input', placeholder: '...', rows: 6 }
// Password input
{ id: 'apiKey', title: 'API Key', type: 'short-input', password: true }
```
### Selection Inputs
```typescript
// Dropdown (static options)
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Create', id: 'create' },
{ label: 'Update', id: 'update' },
],
value: () => 'create', // Default value function
}
// Combobox (searchable dropdown)
{
id: 'field',
title: 'Label',
type: 'combobox',
options: [...],
searchable: true,
}
```
### Code/JSON Inputs
```typescript
{
id: 'code',
title: 'Code',
type: 'code',
language: 'javascript', // 'javascript' | 'json' | 'python'
placeholder: '// Enter code...',
}
```
### OAuth/Credentials
```typescript
{
id: 'credential',
title: 'Account',
type: 'oauth-input',
serviceId: '{service}', // Must match OAuth provider service key
requiredScopes: getScopesForService('{service}'), // Import from @/lib/oauth/utils
placeholder: 'Select account',
required: true,
}
```
**Scopes:** Always use `getScopesForService(serviceId)` from `@/lib/oauth/utils` for `requiredScopes`. Never hardcode scope arrays — the single source of truth is `OAUTH_PROVIDERS` in `lib/oauth/oauth.ts`.
**Scope descriptions:** When adding a new OAuth provider, also add human-readable descriptions for all scopes in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`.
### Selectors (with dynamic options)
```typescript
// Channel selector (Slack, Discord, etc.)
{
id: 'channel',
title: 'Channel',
type: 'channel-selector',
serviceId: '{service}',
placeholder: 'Select channel',
dependsOn: ['credential'],
}
// Project selector (Jira, etc.)
{
id: 'project',
title: 'Project',
type: 'project-selector',
serviceId: '{service}',
dependsOn: ['credential'],
}
// File selector (Google Drive, etc.)
{
id: 'file',
title: 'File',
type: 'file-selector',
serviceId: '{service}',
mimeType: 'application/pdf',
dependsOn: ['credential'],
}
// User selector
{
id: 'user',
title: 'User',
type: 'user-selector',
serviceId: '{service}',
dependsOn: ['credential'],
}
```
### Other Types
```typescript
// Switch/toggle
{ id: 'enabled', type: 'switch' }
// Slider
{ id: 'temperature', title: 'Temperature', type: 'slider', min: 0, max: 2, step: 0.1 }
// Table (key-value pairs)
{ id: 'headers', title: 'Headers', type: 'table', columns: ['Key', 'Value'] }
// File upload
{
id: 'files',
title: 'Attachments',
type: 'file-upload',
multiple: true,
acceptedTypes: 'image/*,application/pdf',
}
```
## File Input Handling
When your block accepts file uploads, use the basic/advanced mode pattern with `normalizeFileInput`.
### Basic/Advanced File Pattern
```typescript
// Basic mode: Visual file upload
{
id: 'uploadFile',
title: 'File',
type: 'file-upload',
canonicalParamId: 'file', // Both map to 'file' param
placeholder: 'Upload file',
mode: 'basic',
multiple: false,
required: true,
condition: { field: 'operation', value: 'upload' },
},
// Advanced mode: Reference from other blocks
{
id: 'fileRef',
title: 'File',
type: 'short-input',
canonicalParamId: 'file', // Both map to 'file' param
placeholder: 'Reference file (e.g., {{file_block.output}})',
mode: 'advanced',
required: true,
condition: { field: 'operation', value: 'upload' },
},
```
**Critical constraints:**
- `canonicalParamId` must NOT match any subblock's `id` in the same block
- Values are stored under subblock `id`, not `canonicalParamId`
### Normalizing File Input in tools.config
Use `normalizeFileInput` to handle all input variants:
```typescript
import { normalizeFileInput } from '@/blocks/utils'
tools: {
access: ['service_upload'],
config: {
tool: (params) => {
// Check all field IDs: uploadFile (basic), fileRef (advanced), fileContent (legacy)
const normalizedFile = normalizeFileInput(
params.uploadFile || params.fileRef || params.fileContent,
{ single: true }
)
if (normalizedFile) {
params.file = normalizedFile
}
return `service_${params.operation}`
},
},
}
```
**Why this pattern?**
- Values come through as `params.uploadFile` or `params.fileRef` (the subblock IDs)
- `canonicalParamId` only controls UI/schema mapping, not runtime values
- `normalizeFileInput` handles JSON strings from advanced mode template resolution
### File Input Types in `inputs`
Use `type: 'json'` for file inputs:
```typescript
inputs: {
uploadFile: { type: 'json', description: 'Uploaded file (UserFile)' },
fileRef: { type: 'json', description: 'File reference from previous block' },
// Legacy field for backwards compatibility
fileContent: { type: 'string', description: 'Legacy: base64 encoded content' },
}
```
### Multiple Files
For multiple file uploads:
```typescript
{
id: 'attachments',
title: 'Attachments',
type: 'file-upload',
multiple: true, // Allow multiple files
maxSize: 25, // Max size in MB per file
acceptedTypes: 'image/*,application/pdf,.doc,.docx',
}
// In tools.config:
const normalizedFiles = normalizeFileInput(
params.attachments || params.attachmentRefs,
// No { single: true } - returns array
)
if (normalizedFiles) {
params.files = normalizedFiles
}
```
## Condition Syntax
Controls when a field is shown based on other field values.
### Simple Condition
```typescript
condition: { field: 'operation', value: 'create' }
// Shows when operation === 'create'
```
### Multiple Values (OR)
```typescript
condition: { field: 'operation', value: ['create', 'update'] }
// Shows when operation is 'create' OR 'update'
```
### Negation
```typescript
condition: { field: 'operation', value: 'delete', not: true }
// Shows when operation !== 'delete'
```
### Compound (AND)
```typescript
condition: {
field: 'operation',
value: 'send',
and: {
field: 'type',
value: 'dm',
not: true,
}
}
// Shows when operation === 'send' AND type !== 'dm'
```
### Complex Example
```typescript
condition: {
field: 'operation',
value: ['list', 'search'],
not: true,
and: {
field: 'authMethod',
value: 'oauth',
}
}
// Shows when operation NOT in ['list', 'search'] AND authMethod === 'oauth'
```
## DependsOn Pattern
Controls when a field is enabled and when its options are refetched.
### Simple Array (all must be set)
```typescript
dependsOn: ['credential']
// Enabled only when credential has a value
// Options refetch when credential changes
dependsOn: ['credential', 'projectId']
// Enabled only when BOTH have values
```
### Complex (all + any)
```typescript
dependsOn: {
all: ['authMethod'], // All must be set
any: ['credential', 'apiKey'] // At least one must be set
}
// Enabled when authMethod is set AND (credential OR apiKey is set)
```
## Required Pattern
Can be boolean or condition-based.
### Simple Boolean
```typescript
required: true
required: false
```
### Conditional Required
```typescript
required: { field: 'operation', value: 'create' }
// Required only when operation === 'create'
required: { field: 'operation', value: ['create', 'update'] }
// Required when operation is 'create' OR 'update'
```
## Mode Pattern (Basic vs Advanced)
Controls which UI view shows the field.
### Mode Options
- `'basic'` - Only in basic view (default UI)
- `'advanced'` - Only in advanced view
- `'both'` - Both views (default if not specified)
- `'trigger'` - Only in trigger configuration
### canonicalParamId Pattern
Maps multiple UI fields to a single serialized parameter:
```typescript
// Basic mode: Visual selector
{
id: 'channel',
title: 'Channel',
type: 'channel-selector',
mode: 'basic',
canonicalParamId: 'channel', // Both map to 'channel' param
dependsOn: ['credential'],
}
// Advanced mode: Manual input
{
id: 'channelId',
title: 'Channel ID',
type: 'short-input',
mode: 'advanced',
canonicalParamId: 'channel', // Both map to 'channel' param
placeholder: 'Enter channel ID manually',
}
```
**How it works:**
- In basic mode: `channel` selector value → `params.channel`
- In advanced mode: `channelId` input value → `params.channel`
- The serializer consolidates based on current mode
**Critical constraints:**
- `canonicalParamId` must NOT match any other subblock's `id` in the same block (causes conflicts)
- `canonicalParamId` must be unique per block (only one basic/advanced pair per canonicalParamId)
- ONLY use `canonicalParamId` to link basic/advanced mode alternatives for the same logical parameter
- Do NOT use it for any other purpose
## WandConfig Pattern
Enables AI-assisted field generation.
```typescript
{
id: 'query',
title: 'Query',
type: 'code',
language: 'json',
wandConfig: {
enabled: true,
prompt: 'Generate a query based on the user request. Return ONLY the JSON.',
placeholder: 'Describe what you want to query...',
generationType: 'json-object', // Optional: affects AI behavior
maintainHistory: true, // Optional: keeps conversation context
},
}
```
### Generation Types
- `'javascript-function-body'` - JS code generation
- `'json-object'` - Raw JSON (adds "no markdown" instruction)
- `'json-schema'` - JSON Schema definitions
- `'sql-query'` - SQL statements
- `'timestamp'` - Adds current date/time context
## Tools Configuration
**Important:** `tools.config.tool` runs during serialization before variable resolution. Put `Number()` and other type coercions in `tools.config.params` instead, which runs at execution time after variables are resolved.
**Preferred:** Use tool names directly as dropdown option IDs to avoid switch cases:
```typescript
// Dropdown options use tool IDs directly
options: [
{ label: 'Create', id: 'service_create' },
{ label: 'Read', id: 'service_read' },
]
// Tool selector just returns the operation value
tool: (params) => params.operation,
```
### With Parameter Transformation
```typescript
tools: {
access: ['service_action'],
config: {
tool: (params) => 'service_action',
params: (params) => ({
id: params.resourceId,
data: typeof params.data === 'string' ? JSON.parse(params.data) : params.data,
}),
},
}
```
### V2 Versioned Tool Selector
```typescript
import { createVersionedToolSelector } from '@/blocks/utils'
tools: {
access: [
'service_create_v2',
'service_read_v2',
'service_update_v2',
],
config: {
tool: createVersionedToolSelector({
baseToolSelector: (params) => `service_${params.operation}`,
suffix: '_v2',
fallbackToolId: 'service_create_v2',
}),
},
}
```
## Outputs Definition
**IMPORTANT:** Block outputs have a simpler schema than tool outputs. Block outputs do NOT support:
- `optional: true` - This is only for tool outputs
- `items` property - This is only for tool outputs with array types
Block outputs only support:
- `type` - The data type ('string', 'number', 'boolean', 'json', 'array')
- `description` - Human readable description
- Nested object structure (for complex types)
```typescript
outputs: {
// Simple outputs
id: { type: 'string', description: 'Resource ID' },
success: { type: 'boolean', description: 'Whether operation succeeded' },
// Use type: 'json' for complex objects or arrays (NOT type: 'array' with items)
items: { type: 'json', description: 'List of items' },
metadata: { type: 'json', description: 'Response metadata' },
// Nested outputs (for structured data)
user: {
id: { type: 'string', description: 'User ID' },
name: { type: 'string', description: 'User name' },
email: { type: 'string', description: 'User email' },
},
}
```
### Typed JSON Outputs
When using `type: 'json'` and you know the object shape in advance, **describe the inner fields in the description** so downstream blocks know what properties are available. For well-known, stable objects, use nested output definitions instead:
```typescript
outputs: {
// BAD: Opaque json with no info about what's inside
plan: { type: 'json', description: 'Zone plan information' },
// GOOD: Describe the known fields in the description
plan: {
type: 'json',
description: 'Zone plan information (id, name, price, currency, frequency, is_subscribed)',
},
// BEST: Use nested output definition when the shape is stable and well-known
plan: {
id: { type: 'string', description: 'Plan identifier' },
name: { type: 'string', description: 'Plan name' },
price: { type: 'number', description: 'Plan price' },
currency: { type: 'string', description: 'Price currency' },
},
}
```
Use the nested pattern when:
- The object has a small, stable set of fields (< 10)
- Downstream blocks will commonly access specific properties
- The API response shape is well-documented and unlikely to change
Use `type: 'json'` with a descriptive string when:
- The object has many fields or a dynamic shape
- It represents a list/array of items
- The shape varies by operation
## V2 Block Pattern
When creating V2 blocks (alongside legacy V1):
```typescript
// V1 Block - mark as legacy
export const ServiceBlock: BlockConfig = {
type: 'service',
name: 'Service (Legacy)',
hideFromToolbar: true, // Hide from toolbar
// ... rest of config
}
// V2 Block - visible, uses V2 tools
export const ServiceV2Block: BlockConfig = {
type: 'service_v2',
name: 'Service', // Clean name
hideFromToolbar: false, // Visible
subBlocks: ServiceBlock.subBlocks, // Reuse UI
tools: {
access: ServiceBlock.tools?.access?.map(id => `${id}_v2`) || [],
config: {
tool: createVersionedToolSelector({
baseToolSelector: (params) => (ServiceBlock.tools?.config as any)?.tool(params),
suffix: '_v2',
fallbackToolId: 'service_default_v2',
}),
params: ServiceBlock.tools?.config?.params,
},
},
outputs: {
// Flat, API-aligned outputs (not wrapped in content/metadata)
},
}
```
## Registering Blocks
After creating the block, remind the user to:
1. Import in `apps/sim/blocks/registry.ts`
2. Add to the `registry` object (alphabetically):
```typescript
import { ServiceBlock } from '@/blocks/blocks/service'
export const registry: Record<string, BlockConfig> = {
// ... existing blocks ...
service: ServiceBlock,
}
```
## Complete Example
```typescript
import { ServiceIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import { getScopesForService } from '@/lib/oauth/utils'
export const ServiceBlock: BlockConfig = {
type: 'service',
name: 'Service',
description: 'Integrate with Service API',
longDescription: 'Full description for documentation...',
docsLink: 'https://docs.sim.ai/tools/service',
category: 'tools',
bgColor: '#FF6B6B',
icon: ServiceIcon,
authMode: AuthMode.OAuth,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Create', id: 'create' },
{ label: 'Read', id: 'read' },
{ label: 'Update', id: 'update' },
{ label: 'Delete', id: 'delete' },
],
value: () => 'create',
},
{
id: 'credential',
title: 'Service Account',
type: 'oauth-input',
serviceId: 'service',
requiredScopes: getScopesForService('service'),
placeholder: 'Select account',
required: true,
},
{
id: 'resourceId',
title: 'Resource ID',
type: 'short-input',
placeholder: 'Enter resource ID',
condition: { field: 'operation', value: ['read', 'update', 'delete'] },
required: { field: 'operation', value: ['read', 'update', 'delete'] },
},
{
id: 'name',
title: 'Name',
type: 'short-input',
placeholder: 'Resource name',
condition: { field: 'operation', value: ['create', 'update'] },
required: { field: 'operation', value: 'create' },
},
],
tools: {
access: ['service_create', 'service_read', 'service_update', 'service_delete'],
config: {
tool: (params) => `service_${params.operation}`,
},
},
outputs: {
id: { type: 'string', description: 'Resource ID' },
name: { type: 'string', description: 'Resource name' },
createdAt: { type: 'string', description: 'Creation timestamp' },
},
}
```
## Connecting Blocks with Triggers
If the service supports webhooks, connect the block to its triggers.
```typescript
import { getTrigger } from '@/triggers'
export const ServiceBlock: BlockConfig = {
// ... basic config ...
triggers: {
enabled: true,
available: ['service_event_a', 'service_event_b', 'service_webhook'],
},
subBlocks: [
// Tool subBlocks first...
{ id: 'operation', /* ... */ },
// Then spread trigger subBlocks
...getTrigger('service_event_a').subBlocks,
...getTrigger('service_event_b').subBlocks,
...getTrigger('service_webhook').subBlocks,
],
}
```
See the `/add-trigger` skill for creating triggers.
## Icon Requirement
If the icon doesn't already exist in `@/components/icons.tsx`, **do NOT search for it yourself**. After completing the block, ask the user to provide the SVG:
```
The block is complete, but I need an icon for {Service}.
Please provide the SVG and I'll convert it to a React component.
You can usually find this in the service's brand/press kit page, or copy it from their website.
```
## Advanced Mode for Optional Fields
Optional fields that are rarely used should be set to `mode: 'advanced'` so they don't clutter the basic UI. This includes:
- Pagination tokens
- Time range filters (start/end time)
- Sort order options
- Reply settings
- Rarely used IDs (e.g., reply-to tweet ID, quote tweet ID)
- Max results / limits
```typescript
{
id: 'startTime',
title: 'Start Time',
type: 'short-input',
placeholder: 'ISO 8601 timestamp',
condition: { field: 'operation', value: ['search', 'list'] },
mode: 'advanced', // Rarely used, hide from basic view
}
```
## WandConfig for Complex Inputs
Use `wandConfig` for fields that are hard to fill out manually, such as timestamps, comma-separated lists, and complex query strings. This gives users an AI-assisted input experience.
```typescript
// Timestamps - use generationType: 'timestamp' to inject current date context
{
id: 'startTime',
title: 'Start Time',
type: 'short-input',
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: 'Generate an ISO 8601 timestamp based on the user description. Return ONLY the timestamp string.',
generationType: 'timestamp',
},
}
// Comma-separated lists - simple prompt without generationType
{
id: 'mediaIds',
title: 'Media IDs',
type: 'short-input',
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: 'Generate a comma-separated list of media IDs. Return ONLY the comma-separated values.',
},
}
```
## Naming Convention
All tool IDs referenced in `tools.access` and returned by `tools.config.tool` MUST use `snake_case` (e.g., `x_create_tweet`, `slack_send_message`). Never use camelCase or PascalCase.
## Checklist Before Finishing
- [ ] All subBlocks have `id`, `title` (except switch), and `type`
- [ ] Conditions use correct syntax (field, value, not, and)
- [ ] DependsOn set for fields that need other values
- [ ] Required fields marked correctly (boolean or condition)
- [ ] OAuth inputs have correct `serviceId` and `requiredScopes: getScopesForService(serviceId)`
- [ ] Scope descriptions added to `SCOPE_DESCRIPTIONS` in `lib/oauth/utils.ts` for any new scopes
- [ ] Tools.access lists all tool IDs (snake_case)
- [ ] Tools.config.tool returns correct tool ID (snake_case)
- [ ] Outputs match tool outputs
- [ ] Block registered in registry.ts
- [ ] If icon missing: asked user to provide SVG
- [ ] If triggers exist: `triggers` config set, trigger subBlocks spread
- [ ] Optional/rarely-used fields set to `mode: 'advanced'`
- [ ] Timestamps and complex inputs have `wandConfig` enabled
## Final Validation (Required)
After creating the block, you MUST validate it against every tool it references:
1. **Read every tool definition** that appears in `tools.access` — do not skip any
2. **For each tool, verify the block has correct:**
- SubBlock inputs that cover all required tool params (with correct `condition` to show for that operation)
- SubBlock input types that match the tool param types (e.g., dropdown for enums, short-input for strings)
- `tools.config.params` correctly maps subBlock IDs to tool param names (if they differ)
- Type coercions in `tools.config.params` for any params that need conversion (Number(), Boolean(), JSON.parse())
3. **Verify block outputs** cover the key fields returned by all tools
4. **Verify conditions** — each subBlock should only show for the operations that actually use it

View File

@@ -0,0 +1,437 @@
---
description: Add a knowledge base connector for syncing documents from an external source
argument-hint: <service-name> [api-docs-url]
---
# Add Connector Skill
You are an expert at adding knowledge base connectors to Sim. A connector syncs documents from an external source (Confluence, Google Drive, Notion, etc.) into a knowledge base.
## Your Task
When the user asks you to create a connector:
1. Use Context7 or WebFetch to read the service's API documentation
2. Determine the auth mode: **OAuth** (if Sim already has an OAuth provider for the service) or **API key** (if the service uses API key / Bearer token auth)
3. Create the connector directory and config
4. Register it in the connector registry
## Directory Structure
Create files in `apps/sim/connectors/{service}/`:
```
connectors/{service}/
├── index.ts # Barrel export
└── {service}.ts # ConnectorConfig definition
```
## Authentication
Connectors use a discriminated union for auth config (`ConnectorAuthConfig` in `connectors/types.ts`):
```typescript
type ConnectorAuthConfig =
| { mode: 'oauth'; provider: OAuthService; requiredScopes?: string[] }
| { mode: 'apiKey'; label?: string; placeholder?: string }
```
### OAuth mode
For services with existing OAuth providers in `apps/sim/lib/oauth/types.ts`. The `provider` must match an `OAuthService`. The modal shows a credential picker and handles token refresh automatically.
### API key mode
For services that use API key / Bearer token auth. The modal shows a password input with the configured `label` and `placeholder`. The API key is encrypted at rest using AES-256-GCM and stored in a dedicated `encryptedApiKey` column on the connector record. The sync engine decrypts it automatically — connectors receive the raw access token in `listDocuments`, `getDocument`, and `validateConfig`.
## ConnectorConfig Structure
### OAuth connector example
```typescript
import { createLogger } from '@sim/logger'
import { {Service}Icon } from '@/components/icons'
import { fetchWithRetry } from '@/lib/knowledge/documents/utils'
import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types'
const logger = createLogger('{Service}Connector')
export const {service}Connector: ConnectorConfig = {
id: '{service}',
name: '{Service}',
description: 'Sync documents from {Service} into your knowledge base',
version: '1.0.0',
icon: {Service}Icon,
auth: {
mode: 'oauth',
provider: '{service}', // Must match OAuthService in lib/oauth/types.ts
requiredScopes: ['read:...'],
},
configFields: [
// Rendered dynamically by the add-connector modal UI
// Supports 'short-input' and 'dropdown' types
],
listDocuments: async (accessToken, sourceConfig, cursor) => {
// Paginate via cursor, extract text, compute SHA-256 hash
// Return { documents: ExternalDocument[], nextCursor?, hasMore }
},
getDocument: async (accessToken, sourceConfig, externalId) => {
// Return ExternalDocument or null
},
validateConfig: async (accessToken, sourceConfig) => {
// Return { valid: true } or { valid: false, error: 'message' }
},
// Optional: map source metadata to semantic tag keys (translated to slots by sync engine)
mapTags: (metadata) => {
// Return Record<string, unknown> with keys matching tagDefinitions[].id
},
}
```
### API key connector example
```typescript
export const {service}Connector: ConnectorConfig = {
id: '{service}',
name: '{Service}',
description: 'Sync documents from {Service} into your knowledge base',
version: '1.0.0',
icon: {Service}Icon,
auth: {
mode: 'apiKey',
label: 'API Key', // Shown above the input field
placeholder: 'Enter your {Service} API key', // Input placeholder
},
configFields: [ /* ... */ ],
listDocuments: async (accessToken, sourceConfig, cursor) => { /* ... */ },
getDocument: async (accessToken, sourceConfig, externalId) => { /* ... */ },
validateConfig: async (accessToken, sourceConfig) => { /* ... */ },
}
```
## ConfigField Types
The add-connector modal renders these automatically — no custom UI needed.
Three field types are supported: `short-input`, `dropdown`, and `selector`.
```typescript
// Text input
{
id: 'domain',
title: 'Domain',
type: 'short-input',
placeholder: 'yoursite.example.com',
required: true,
}
// Dropdown (static options)
{
id: 'contentType',
title: 'Content Type',
type: 'dropdown',
required: false,
options: [
{ label: 'Pages only', id: 'page' },
{ label: 'Blog posts only', id: 'blogpost' },
{ label: 'All content', id: 'all' },
],
}
```
## Dynamic Selectors (Canonical Pairs)
Use `type: 'selector'` to fetch options dynamically from the existing selector registry (`hooks/selectors/registry.ts`). Selectors are always paired with a manual fallback input using the **canonical pair** pattern — a `selector` field (basic mode) and a `short-input` field (advanced mode) linked by `canonicalParamId`.
The user sees a toggle button (ArrowLeftRight) to switch between the selector dropdown and manual text input. On submit, the modal resolves each canonical pair to the active mode's value, keyed by `canonicalParamId`.
### Rules
1. **Every selector field MUST have a canonical pair** — a corresponding `short-input` (or `dropdown`) field with the same `canonicalParamId` and `mode: 'advanced'`.
2. **`required` must be set identically on both fields** in a pair. If the selector is required, the manual input must also be required.
3. **`canonicalParamId` must match the key the connector expects in `sourceConfig`** (e.g. `baseId`, `channel`, `teamId`). The advanced field's `id` should typically match `canonicalParamId`.
4. **`dependsOn` references the selector field's `id`**, not the `canonicalParamId`. The modal propagates dependency clearing across canonical siblings automatically — changing either field in a parent pair clears dependent children.
### Selector canonical pair example (Airtable base → table cascade)
```typescript
configFields: [
// Base: selector (basic) + manual (advanced)
{
id: 'baseSelector',
title: 'Base',
type: 'selector',
selectorKey: 'airtable.bases', // Must exist in hooks/selectors/registry.ts
canonicalParamId: 'baseId',
mode: 'basic',
placeholder: 'Select a base',
required: true,
},
{
id: 'baseId',
title: 'Base ID',
type: 'short-input',
canonicalParamId: 'baseId',
mode: 'advanced',
placeholder: 'e.g. appXXXXXXXXXXXXXX',
required: true,
},
// Table: selector depends on base (basic) + manual (advanced)
{
id: 'tableSelector',
title: 'Table',
type: 'selector',
selectorKey: 'airtable.tables',
canonicalParamId: 'tableIdOrName',
mode: 'basic',
dependsOn: ['baseSelector'], // References the selector field ID
placeholder: 'Select a table',
required: true,
},
{
id: 'tableIdOrName',
title: 'Table Name or ID',
type: 'short-input',
canonicalParamId: 'tableIdOrName',
mode: 'advanced',
placeholder: 'e.g. Tasks',
required: true,
},
// Non-selector fields stay as-is
{ id: 'maxRecords', title: 'Max Records', type: 'short-input', ... },
]
```
### Selector with domain dependency (Jira/Confluence pattern)
When a selector depends on a plain `short-input` field (no canonical pair), `dependsOn` references that field's `id` directly. The `domain` field's value maps to `SelectorContext.domain` automatically via `SELECTOR_CONTEXT_FIELDS`.
```typescript
configFields: [
{
id: 'domain',
title: 'Jira Domain',
type: 'short-input',
placeholder: 'yoursite.atlassian.net',
required: true,
},
{
id: 'projectSelector',
title: 'Project',
type: 'selector',
selectorKey: 'jira.projects',
canonicalParamId: 'projectKey',
mode: 'basic',
dependsOn: ['domain'],
placeholder: 'Select a project',
required: true,
},
{
id: 'projectKey',
title: 'Project Key',
type: 'short-input',
canonicalParamId: 'projectKey',
mode: 'advanced',
placeholder: 'e.g. ENG, PROJ',
required: true,
},
]
```
### How `dependsOn` maps to `SelectorContext`
The connector selector field builds a `SelectorContext` from dependency values. For the mapping to work, each dependency's `canonicalParamId` (or field `id` for non-canonical fields) must exist in `SELECTOR_CONTEXT_FIELDS` (`lib/workflows/subblocks/context.ts`):
```
oauthCredential, domain, teamId, projectId, knowledgeBaseId, planId,
siteId, collectionId, spreadsheetId, fileId, baseId, datasetId, serviceDeskId
```
### Available selector keys
Check `hooks/selectors/types.ts` for the full `SelectorKey` union. Common ones for connectors:
| SelectorKey | Context Deps | Returns |
|-------------|-------------|---------|
| `airtable.bases` | credential | Base ID + name |
| `airtable.tables` | credential, `baseId` | Table ID + name |
| `slack.channels` | credential | Channel ID + name |
| `gmail.labels` | credential | Label ID + name |
| `google.calendar` | credential | Calendar ID + name |
| `linear.teams` | credential | Team ID + name |
| `linear.projects` | credential, `teamId` | Project ID + name |
| `jira.projects` | credential, `domain` | Project key + name |
| `confluence.spaces` | credential, `domain` | Space key + name |
| `notion.databases` | credential | Database ID + name |
| `asana.workspaces` | credential | Workspace GID + name |
| `microsoft.teams` | credential | Team ID + name |
| `microsoft.channels` | credential, `teamId` | Channel ID + name |
| `webflow.sites` | credential | Site ID + name |
| `outlook.folders` | credential | Folder ID + name |
## ExternalDocument Shape
Every document returned from `listDocuments`/`getDocument` must include:
```typescript
{
externalId: string // Source-specific unique ID
title: string // Document title
content: string // Extracted plain text
mimeType: 'text/plain' // Always text/plain (content is extracted)
contentHash: string // SHA-256 of content (change detection)
sourceUrl?: string // Link back to original (stored on document record)
metadata?: Record<string, unknown> // Source-specific data (fed to mapTags)
}
```
## Content Hashing (Required)
The sync engine uses content hashes for change detection:
```typescript
async function computeContentHash(content: string): Promise<string> {
const data = new TextEncoder().encode(content)
const hashBuffer = await crypto.subtle.digest('SHA-256', data)
return Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('')
}
```
## tagDefinitions — Declared Tag Definitions
Declare which tags the connector populates using semantic IDs. Shown in the add-connector modal as opt-out checkboxes.
On connector creation, slots are **dynamically assigned** via `getNextAvailableSlot` — connectors never hardcode slot names.
```typescript
tagDefinitions: [
{ id: 'labels', displayName: 'Labels', fieldType: 'text' },
{ id: 'version', displayName: 'Version', fieldType: 'number' },
{ id: 'lastModified', displayName: 'Last Modified', fieldType: 'date' },
],
```
Each entry has:
- `id`: Semantic key matching a key returned by `mapTags` (e.g. `'labels'`, `'version'`)
- `displayName`: Human-readable name shown in the UI (e.g. "Labels", "Last Modified")
- `fieldType`: `'text'` | `'number'` | `'date'` | `'boolean'` — determines which slot pool to draw from
Users can opt out of specific tags in the modal. Disabled IDs are stored in `sourceConfig.disabledTagIds`.
The assigned mapping (`semantic id → slot`) is stored in `sourceConfig.tagSlotMapping`.
## mapTags — Metadata to Semantic Keys
Maps source metadata to semantic tag keys. Required if `tagDefinitions` is set.
The sync engine calls this automatically and translates semantic keys to actual DB slots
using the `tagSlotMapping` stored on the connector.
Return keys must match the `id` values declared in `tagDefinitions`.
```typescript
mapTags: (metadata: Record<string, unknown>): Record<string, unknown> => {
const result: Record<string, unknown> = {}
// Validate arrays before casting — metadata may be malformed
const labels = Array.isArray(metadata.labels) ? (metadata.labels as string[]) : []
if (labels.length > 0) result.labels = labels.join(', ')
// Validate numbers — guard against NaN
if (metadata.version != null) {
const num = Number(metadata.version)
if (!Number.isNaN(num)) result.version = num
}
// Validate dates — guard against Invalid Date
if (typeof metadata.lastModified === 'string') {
const date = new Date(metadata.lastModified)
if (!Number.isNaN(date.getTime())) result.lastModified = date
}
return result
}
```
## External API Calls — Use `fetchWithRetry`
All external API calls must use `fetchWithRetry` from `@/lib/knowledge/documents/utils` instead of raw `fetch()`. This provides exponential backoff with retries on 429/502/503/504 errors. It returns a standard `Response` — all `.ok`, `.json()`, `.text()` checks work unchanged.
For `validateConfig` (user-facing, called on save), pass `VALIDATE_RETRY_OPTIONS` to cap wait time at ~7s. Background operations (`listDocuments`, `getDocument`) use the built-in defaults (5 retries, ~31s max).
```typescript
import { VALIDATE_RETRY_OPTIONS, fetchWithRetry } from '@/lib/knowledge/documents/utils'
// Background sync — use defaults
const response = await fetchWithRetry(url, {
method: 'GET',
headers: { Authorization: `Bearer ${accessToken}` },
})
// validateConfig — tighter retry budget
const response = await fetchWithRetry(url, { ... }, VALIDATE_RETRY_OPTIONS)
```
## sourceUrl
If `ExternalDocument.sourceUrl` is set, the sync engine stores it on the document record. Always construct the full URL (not a relative path).
## Sync Engine Behavior (Do Not Modify)
The sync engine (`lib/knowledge/connectors/sync-engine.ts`) is connector-agnostic. It:
1. Calls `listDocuments` with pagination until `hasMore` is false
2. Compares `contentHash` to detect new/changed/unchanged documents
3. Stores `sourceUrl` and calls `mapTags` on insert/update automatically
4. Handles soft-delete of removed documents
5. Resolves access tokens automatically — OAuth tokens are refreshed, API keys are decrypted from the `encryptedApiKey` column
You never need to modify the sync engine when adding a connector.
## Icon
The `icon` field on `ConnectorConfig` is used throughout the UI — in the connector list, the add-connector modal, and as the document icon in the knowledge base table (replacing the generic file type icon for connector-sourced documents). The icon is read from `CONNECTOR_REGISTRY[connectorType].icon` at runtime — no separate icon map to maintain.
If the service already has an icon in `apps/sim/components/icons.tsx` (from a tool integration), reuse it. Otherwise, ask the user to provide the SVG.
## Registering
Add one line to `apps/sim/connectors/registry.ts`:
```typescript
import { {service}Connector } from '@/connectors/{service}'
export const CONNECTOR_REGISTRY: ConnectorRegistry = {
// ... existing connectors ...
{service}: {service}Connector,
}
```
## Reference Implementations
- **OAuth**: `apps/sim/connectors/confluence/confluence.ts` — multiple config field types, `mapTags`, label fetching
- **API key**: `apps/sim/connectors/fireflies/fireflies.ts` — GraphQL API with Bearer token auth
## Checklist
- [ ] Created `connectors/{service}/{service}.ts` with full ConnectorConfig
- [ ] Created `connectors/{service}/index.ts` barrel export
- [ ] **Auth configured correctly:**
- OAuth: `auth.provider` matches an existing `OAuthService` in `lib/oauth/types.ts`
- API key: `auth.label` and `auth.placeholder` set appropriately
- [ ] **Selector fields configured correctly (if applicable):**
- Every `type: 'selector'` field has a canonical pair (`short-input` or `dropdown` with same `canonicalParamId` and `mode: 'advanced'`)
- `required` is identical on both fields in each canonical pair
- `selectorKey` exists in `hooks/selectors/registry.ts`
- `dependsOn` references selector field IDs (not `canonicalParamId`)
- Dependency `canonicalParamId` values exist in `SELECTOR_CONTEXT_FIELDS`
- [ ] `listDocuments` handles pagination and computes content hashes
- [ ] `sourceUrl` set on each ExternalDocument (full URL, not relative)
- [ ] `metadata` includes source-specific data for tag mapping
- [ ] `tagDefinitions` declared for each semantic key returned by `mapTags`
- [ ] `mapTags` implemented if source has useful metadata (labels, dates, versions)
- [ ] `validateConfig` verifies the source is accessible
- [ ] All external API calls use `fetchWithRetry` (not raw `fetch`)
- [ ] All optional config fields validated in `validateConfig`
- [ ] Icon exists in `components/icons.tsx` (or asked user to provide SVG)
- [ ] Registered in `connectors/registry.ts`

View File

@@ -0,0 +1,760 @@
---
description: Add a complete integration to Sim (tools, block, icon, registration)
argument-hint: <service-name> [api-docs-url]
---
# Add Integration Skill
You are an expert at adding complete integrations to Sim. This skill orchestrates the full process of adding a new service integration.
## Overview
Adding an integration involves these steps in order:
1. **Research** - Read the service's API documentation
2. **Create Tools** - Build tool configurations for each API operation
3. **Create Block** - Build the block UI configuration
4. **Add Icon** - Add the service's brand icon
5. **Create Triggers** (optional) - If the service supports webhooks
6. **Register** - Register tools, block, and triggers in their registries
7. **Generate Docs** - Run the docs generation script
## Step 1: Research the API
Before writing any code:
1. Use Context7 to find official documentation: `mcp__plugin_context7_context7__resolve-library-id`
2. Or use WebFetch to read API docs directly
3. Identify:
- Authentication method (OAuth, API Key, both)
- Available operations (CRUD, search, etc.)
- Required vs optional parameters
- Response structures
## Step 2: Create Tools
### Directory Structure
```
apps/sim/tools/{service}/
├── index.ts # Barrel exports
├── types.ts # TypeScript interfaces
├── {action1}.ts # Tool for action 1
├── {action2}.ts # Tool for action 2
└── ...
```
### Key Patterns
**types.ts:**
```typescript
import type { ToolResponse } from '@/tools/types'
export interface {Service}{Action}Params {
accessToken: string // For OAuth services
// OR
apiKey: string // For API key services
requiredParam: string
optionalParam?: string
}
export interface {Service}Response extends ToolResponse {
output: {
// Define output structure
}
}
```
**Tool file pattern:**
```typescript
export const {service}{Action}Tool: ToolConfig<Params, Response> = {
id: '{service}_{action}',
name: '{Service} {Action}',
description: '...',
version: '1.0.0',
oauth: { required: true, provider: '{service}' }, // If OAuth
params: {
accessToken: { type: 'string', required: true, visibility: 'hidden', description: '...' },
// ... other params
},
request: { url, method, headers, body },
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
field: data.field ?? null, // Always handle nullables
},
}
},
outputs: { /* ... */ },
}
```
### Critical Rules
- `visibility: 'hidden'` for OAuth tokens
- `visibility: 'user-only'` for API keys and user credentials
- `visibility: 'user-or-llm'` for operation parameters
- Always use `?? null` for nullable API response fields
- Always use `?? []` for optional array fields
- Set `optional: true` for outputs that may not exist
- Never output raw JSON dumps - extract meaningful fields
- When using `type: 'json'` and you know the object shape, define `properties` with the inner fields so downstream consumers know the structure. Only use bare `type: 'json'` when the shape is truly dynamic
## Step 3: Create Block
### File Location
`apps/sim/blocks/blocks/{service}.ts`
### Block Structure
```typescript
import { {Service}Icon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import { getScopesForService } from '@/lib/oauth/utils'
export const {Service}Block: BlockConfig = {
type: '{service}',
name: '{Service}',
description: '...',
longDescription: '...',
docsLink: 'https://docs.sim.ai/tools/{service}',
category: 'tools',
bgColor: '#HEXCOLOR',
icon: {Service}Icon,
authMode: AuthMode.OAuth, // or AuthMode.ApiKey
subBlocks: [
// Operation dropdown
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Operation 1', id: 'action1' },
{ label: 'Operation 2', id: 'action2' },
],
value: () => 'action1',
},
// Credential field
{
id: 'credential',
title: '{Service} Account',
type: 'oauth-input',
serviceId: '{service}',
requiredScopes: getScopesForService('{service}'),
required: true,
},
// Conditional fields per operation
// ...
],
tools: {
access: ['{service}_action1', '{service}_action2'],
config: {
tool: (params) => `{service}_${params.operation}`,
},
},
outputs: { /* ... */ },
}
```
### Key SubBlock Patterns
**Condition-based visibility:**
```typescript
{
id: 'resourceId',
title: 'Resource ID',
type: 'short-input',
condition: { field: 'operation', value: ['read', 'update', 'delete'] },
required: { field: 'operation', value: ['read', 'update', 'delete'] },
}
```
**DependsOn for cascading selectors:**
```typescript
{
id: 'project',
type: 'project-selector',
dependsOn: ['credential'],
},
{
id: 'issue',
type: 'file-selector',
dependsOn: ['credential', 'project'],
}
```
**Basic/Advanced mode for dual UX:**
```typescript
// Basic: Visual selector
{
id: 'channel',
type: 'channel-selector',
mode: 'basic',
canonicalParamId: 'channel',
dependsOn: ['credential'],
},
// Advanced: Manual input
{
id: 'channelId',
type: 'short-input',
mode: 'advanced',
canonicalParamId: 'channel',
}
```
**Critical Canonical Param Rules:**
- `canonicalParamId` must NOT match any subblock's `id` in the block
- `canonicalParamId` must be unique per operation/condition context
- Only use `canonicalParamId` to link basic/advanced alternatives for the same logical parameter
- `mode` only controls UI visibility, NOT serialization. Without `canonicalParamId`, both basic and advanced field values would be sent
- Every subblock `id` must be unique within the block. Duplicate IDs cause conflicts even with different conditions
- **Required consistency:** If one subblock in a canonical group has `required: true`, ALL subblocks in that group must have `required: true` (prevents bypassing validation by switching modes)
- **Inputs section:** Must list canonical param IDs (e.g., `fileId`), NOT raw subblock IDs (e.g., `fileSelector`, `manualFileId`)
- **Params function:** Must use canonical param IDs, NOT raw subblock IDs (raw IDs are deleted after canonical transformation)
## Step 4: Add Icon
### File Location
`apps/sim/components/icons.tsx`
### Pattern
```typescript
export function {Service}Icon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{/* SVG paths from user-provided SVG */}
</svg>
)
}
```
### Getting Icons
**Do NOT search for icons yourself.** At the end of implementation, ask the user to provide the SVG:
```
I've completed the integration. Before I can add the icon, please provide the SVG for {Service}.
You can usually find this in the service's brand/press kit page, or copy it from their website.
Paste the SVG code here and I'll convert it to a React component.
```
Once the user provides the SVG:
1. Extract the SVG paths/content
2. Create a React component that spreads props
3. Ensure viewBox is preserved from the original SVG
## Step 5: Create Triggers (Optional)
If the service supports webhooks, create triggers using the generic `buildTriggerSubBlocks` helper.
### Directory Structure
```
apps/sim/triggers/{service}/
├── index.ts # Barrel exports
├── utils.ts # Trigger options, setup instructions, extra fields
├── {event_a}.ts # Primary trigger (includes dropdown)
├── {event_b}.ts # Secondary triggers (no dropdown)
└── webhook.ts # Generic webhook (optional)
```
### Key Pattern
```typescript
import { buildTriggerSubBlocks } from '@/triggers'
import { {service}TriggerOptions, {service}SetupInstructions, build{Service}ExtraFields } from './utils'
// Primary trigger - includeDropdown: true
export const {service}EventATrigger: TriggerConfig = {
id: '{service}_event_a',
subBlocks: buildTriggerSubBlocks({
triggerId: '{service}_event_a',
triggerOptions: {service}TriggerOptions,
includeDropdown: true, // Only for primary trigger!
setupInstructions: {service}SetupInstructions('Event A'),
extraFields: build{Service}ExtraFields('{service}_event_a'),
}),
// ...
}
// Secondary triggers - no dropdown
export const {service}EventBTrigger: TriggerConfig = {
id: '{service}_event_b',
subBlocks: buildTriggerSubBlocks({
triggerId: '{service}_event_b',
triggerOptions: {service}TriggerOptions,
// No includeDropdown!
setupInstructions: {service}SetupInstructions('Event B'),
extraFields: build{Service}ExtraFields('{service}_event_b'),
}),
// ...
}
```
### Connect to Block
```typescript
import { getTrigger } from '@/triggers'
export const {Service}Block: BlockConfig = {
triggers: {
enabled: true,
available: ['{service}_event_a', '{service}_event_b'],
},
subBlocks: [
// Tool fields...
...getTrigger('{service}_event_a').subBlocks,
...getTrigger('{service}_event_b').subBlocks,
],
}
```
See `/add-trigger` skill for complete documentation.
## Step 6: Register Everything
### Tools Registry (`apps/sim/tools/registry.ts`)
```typescript
// Add import (alphabetically)
import {
{service}Action1Tool,
{service}Action2Tool,
} from '@/tools/{service}'
// Add to tools object (alphabetically)
export const tools: Record<string, ToolConfig> = {
// ... existing tools ...
{service}_action1: {service}Action1Tool,
{service}_action2: {service}Action2Tool,
}
```
### Block Registry (`apps/sim/blocks/registry.ts`)
```typescript
// Add import (alphabetically)
import { {Service}Block } from '@/blocks/blocks/{service}'
// Add to registry (alphabetically)
export const registry: Record<string, BlockConfig> = {
// ... existing blocks ...
{service}: {Service}Block,
}
```
### Trigger Registry (`apps/sim/triggers/registry.ts`) - If triggers exist
```typescript
// Add import (alphabetically)
import {
{service}EventATrigger,
{service}EventBTrigger,
{service}WebhookTrigger,
} from '@/triggers/{service}'
// Add to TRIGGER_REGISTRY (alphabetically)
export const TRIGGER_REGISTRY: TriggerRegistry = {
// ... existing triggers ...
{service}_event_a: {service}EventATrigger,
{service}_event_b: {service}EventBTrigger,
{service}_webhook: {service}WebhookTrigger,
}
```
## Step 7: Generate Docs
Run the documentation generator:
```bash
bun run scripts/generate-docs.ts
```
This creates `apps/docs/content/docs/en/tools/{service}.mdx`
## V2 Integration Pattern
If creating V2 versions (API-aligned outputs):
1. **V2 Tools** - Add `_v2` suffix, version `2.0.0`, flat outputs
2. **V2 Block** - Add `_v2` type, use `createVersionedToolSelector`
3. **V1 Block** - Add `(Legacy)` to name, set `hideFromToolbar: true`
4. **Registry** - Register both versions
```typescript
// In registry
{service}: {Service}Block, // V1 (legacy, hidden)
{service}_v2: {Service}V2Block, // V2 (visible)
```
## Complete Checklist
### Tools
- [ ] Created `tools/{service}/` directory
- [ ] Created `types.ts` with all interfaces
- [ ] Created tool file for each operation
- [ ] All params have correct visibility
- [ ] All nullable fields use `?? null`
- [ ] All optional outputs have `optional: true`
- [ ] Created `index.ts` barrel export
- [ ] Registered all tools in `tools/registry.ts`
### Block
- [ ] Created `blocks/blocks/{service}.ts`
- [ ] Defined operation dropdown with all operations
- [ ] Added credential field with `requiredScopes: getScopesForService('{service}')`
- [ ] Added conditional fields per operation
- [ ] Set up dependsOn for cascading selectors
- [ ] Configured tools.access with all tool IDs
- [ ] Configured tools.config.tool selector
- [ ] Defined outputs matching tool outputs
- [ ] Registered block in `blocks/registry.ts`
- [ ] If triggers: set `triggers.enabled` and `triggers.available`
- [ ] If triggers: spread trigger subBlocks with `getTrigger()`
### OAuth Scopes (if OAuth service)
- [ ] Defined scopes in `lib/oauth/oauth.ts` under `OAUTH_PROVIDERS`
- [ ] Added scope descriptions in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`
- [ ] Used `getCanonicalScopesForProvider()` in `auth.ts` (never hardcode)
- [ ] Used `getScopesForService()` in block `requiredScopes` (never hardcode)
### Icon
- [ ] Asked user to provide SVG
- [ ] Added icon to `components/icons.tsx`
- [ ] Icon spreads props correctly
### Triggers (if service supports webhooks)
- [ ] Created `triggers/{service}/` directory
- [ ] Created `utils.ts` with options, instructions, and extra fields helpers
- [ ] Primary trigger uses `includeDropdown: true`
- [ ] Secondary triggers do NOT have `includeDropdown`
- [ ] All triggers use `buildTriggerSubBlocks` helper
- [ ] Created `index.ts` barrel export
- [ ] Registered all triggers in `triggers/registry.ts`
### Docs
- [ ] Ran `bun run scripts/generate-docs.ts`
- [ ] Verified docs file created
### Final Validation (Required)
- [ ] Read every tool file and cross-referenced inputs/outputs against the API docs
- [ ] Verified block subBlocks cover all required tool params with correct conditions
- [ ] Verified block outputs match what the tools actually return
- [ ] Verified `tools.config.params` correctly maps and coerces all param types
## Example Command
When the user asks to add an integration:
```
User: Add a Stripe integration
You: I'll add the Stripe integration. Let me:
1. First, research the Stripe API using Context7
2. Create the tools for key operations (payments, subscriptions, etc.)
3. Create the block with operation dropdown
4. Register everything
5. Generate docs
6. Ask you for the Stripe icon SVG
[Proceed with implementation...]
[After completing steps 1-5...]
I've completed the Stripe integration. Before I can add the icon, please provide the SVG for Stripe.
You can usually find this in the service's brand/press kit page, or copy it from their website.
Paste the SVG code here and I'll convert it to a React component.
```
## File Handling
When your integration handles file uploads or downloads, follow these patterns to work with `UserFile` objects consistently.
### What is a UserFile?
A `UserFile` is the standard file representation in Sim:
```typescript
interface UserFile {
id: string // Unique identifier
name: string // Original filename
url: string // Presigned URL for download
size: number // File size in bytes
type: string // MIME type (e.g., 'application/pdf')
base64?: string // Optional base64 content (if small file)
key?: string // Internal storage key
context?: object // Storage context metadata
}
```
### File Input Pattern (Uploads)
For tools that accept file uploads, **always route through an internal API endpoint** rather than calling external APIs directly. This ensures proper file content retrieval.
#### 1. Block SubBlocks for File Input
Use the basic/advanced mode pattern:
```typescript
// Basic mode: File upload UI
{
id: 'uploadFile',
title: 'File',
type: 'file-upload',
canonicalParamId: 'file', // Maps to 'file' param
placeholder: 'Upload file',
mode: 'basic',
multiple: false,
required: true,
condition: { field: 'operation', value: 'upload' },
},
// Advanced mode: Reference from previous block
{
id: 'fileRef',
title: 'File',
type: 'short-input',
canonicalParamId: 'file', // Same canonical param
placeholder: 'Reference file (e.g., {{file_block.output}})',
mode: 'advanced',
required: true,
condition: { field: 'operation', value: 'upload' },
},
```
**Critical:** `canonicalParamId` must NOT match any subblock `id`.
#### 2. Normalize File Input in Block Config
In `tools.config.tool`, use `normalizeFileInput` to handle all input variants:
```typescript
import { normalizeFileInput } from '@/blocks/utils'
tools: {
config: {
tool: (params) => {
// Normalize file from basic (uploadFile), advanced (fileRef), or legacy (fileContent)
const normalizedFile = normalizeFileInput(
params.uploadFile || params.fileRef || params.fileContent,
{ single: true }
)
if (normalizedFile) {
params.file = normalizedFile
}
return `{service}_${params.operation}`
},
},
}
```
#### 3. Create Internal API Route
Create `apps/sim/app/api/tools/{service}/{action}/route.ts`:
```typescript
import { createLogger } from '@sim/logger'
import { NextResponse, type NextRequest } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateRequestId } from '@/lib/core/utils/request'
import { FileInputSchema, type RawFileInput } from '@/lib/uploads/utils/file-schemas'
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
const logger = createLogger('{Service}UploadAPI')
const RequestSchema = z.object({
accessToken: z.string(),
file: FileInputSchema.optional().nullable(),
// Legacy field for backwards compatibility
fileContent: z.string().optional().nullable(),
// ... other params
})
export async function POST(request: NextRequest) {
const requestId = generateRequestId()
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
if (!authResult.success) {
return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
}
const body = await request.json()
const data = RequestSchema.parse(body)
let fileBuffer: Buffer
let fileName: string
// Prefer UserFile input, fall back to legacy base64
if (data.file) {
const userFiles = processFilesToUserFiles([data.file as RawFileInput], requestId, logger)
if (userFiles.length === 0) {
return NextResponse.json({ success: false, error: 'Invalid file' }, { status: 400 })
}
const userFile = userFiles[0]
fileBuffer = await downloadFileFromStorage(userFile, requestId, logger)
fileName = userFile.name
} else if (data.fileContent) {
// Legacy: base64 string (backwards compatibility)
fileBuffer = Buffer.from(data.fileContent, 'base64')
fileName = 'file'
} else {
return NextResponse.json({ success: false, error: 'File required' }, { status: 400 })
}
// Now call external API with fileBuffer
const response = await fetch('https://api.{service}.com/upload', {
method: 'POST',
headers: { Authorization: `Bearer ${data.accessToken}` },
body: new Uint8Array(fileBuffer), // Convert Buffer for fetch
})
// ... handle response
}
```
#### 4. Update Tool to Use Internal Route
```typescript
export const {service}UploadTool: ToolConfig<Params, Response> = {
id: '{service}_upload',
// ...
params: {
file: { type: 'file', required: false, visibility: 'user-or-llm' },
fileContent: { type: 'string', required: false, visibility: 'hidden' }, // Legacy
},
request: {
url: '/api/tools/{service}/upload', // Internal route
method: 'POST',
body: (params) => ({
accessToken: params.accessToken,
file: params.file,
fileContent: params.fileContent,
}),
},
}
```
### File Output Pattern (Downloads)
For tools that return files, use `FileToolProcessor` to store files and return `UserFile` objects.
#### In Tool transformResponse
```typescript
import { FileToolProcessor } from '@/executor/utils/file-tool-processor'
transformResponse: async (response, context) => {
const data = await response.json()
// Process file outputs to UserFile objects
const fileProcessor = new FileToolProcessor(context)
const file = await fileProcessor.processFileData({
data: data.content, // base64 or buffer
mimeType: data.mimeType,
filename: data.filename,
})
return {
success: true,
output: { file },
}
}
```
#### In API Route (for complex file handling)
```typescript
// Return file data that FileToolProcessor can handle
return NextResponse.json({
success: true,
output: {
file: {
data: base64Content,
mimeType: 'application/pdf',
filename: 'document.pdf',
},
},
})
```
### Key Helpers Reference
| Helper | Location | Purpose |
|--------|----------|---------|
| `normalizeFileInput` | `@/blocks/utils` | Normalize file params in block config |
| `processFilesToUserFiles` | `@/lib/uploads/utils/file-utils` | Convert raw inputs to UserFile[] |
| `downloadFileFromStorage` | `@/lib/uploads/utils/file-utils.server` | Get file Buffer from UserFile |
| `FileToolProcessor` | `@/executor/utils/file-tool-processor` | Process tool output files |
| `isUserFile` | `@/lib/core/utils/user-file` | Type guard for UserFile objects |
| `FileInputSchema` | `@/lib/uploads/utils/file-schemas` | Zod schema for file validation |
### Advanced Mode for Optional Fields
Optional fields that are rarely used should be set to `mode: 'advanced'` so they don't clutter the basic UI. Examples: pagination tokens, time range filters, sort order, max results, reply settings.
### WandConfig for Complex Inputs
Use `wandConfig` for fields that are hard to fill out manually:
- **Timestamps**: Use `generationType: 'timestamp'` to inject current date context into the AI prompt
- **JSON arrays**: Use `generationType: 'json-object'` for structured data
- **Complex queries**: Use a descriptive prompt explaining the expected format
```typescript
{
id: 'startTime',
title: 'Start Time',
type: 'short-input',
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: 'Generate an ISO 8601 timestamp. Return ONLY the timestamp string.',
generationType: 'timestamp',
},
}
```
### OAuth Scopes (Centralized System)
Scopes are maintained in a single source of truth and reused everywhere:
1. **Define scopes** in `lib/oauth/oauth.ts` under `OAUTH_PROVIDERS[provider].services[service].scopes`
2. **Add descriptions** in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts` for the OAuth modal UI
3. **Reference in auth.ts** using `getCanonicalScopesForProvider(providerId)` from `@/lib/oauth/utils`
4. **Reference in blocks** using `getScopesForService(serviceId)` from `@/lib/oauth/utils`
**Never hardcode scope arrays** in `auth.ts` or block `requiredScopes`. Always import from the centralized source.
```typescript
// In auth.ts (Better Auth config)
scopes: getCanonicalScopesForProvider('{service}'),
// In block credential sub-block
requiredScopes: getScopesForService('{service}'),
```
### Common Gotchas
1. **OAuth serviceId must match** - The `serviceId` in oauth-input must match the OAuth provider configuration
2. **All tool IDs MUST be snake_case** - `stripe_create_payment`, not `stripeCreatePayment`. This applies to tool `id` fields, registry keys, `tools.access` arrays, and `tools.config.tool` return values
3. **Block type is snake_case** - `type: 'stripe'`, not `type: 'Stripe'`
4. **Alphabetical ordering** - Keep imports and registry entries alphabetically sorted
5. **Required can be conditional** - Use `required: { field: 'op', value: 'create' }` instead of always true
6. **DependsOn clears options** - When a dependency changes, selector options are refetched
7. **Never pass Buffer directly to fetch** - Convert to `new Uint8Array(buffer)` for TypeScript compatibility
8. **Always handle legacy file params** - Keep hidden `fileContent` params for backwards compatibility
9. **Optional fields use advanced mode** - Set `mode: 'advanced'` on rarely-used optional fields
10. **Complex inputs need wandConfig** - Timestamps, JSON arrays, and other hard-to-type values should have `wandConfig` enabled
11. **Never hardcode scopes** - Use `getScopesForService()` in blocks and `getCanonicalScopesForProvider()` in auth.ts
12. **Always add scope descriptions** - New scopes must have entries in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`

View File

@@ -0,0 +1,321 @@
---
description: Create tool configurations for a Sim integration by reading API docs
argument-hint: <service-name> [api-docs-url]
---
# Add Tools Skill
You are an expert at creating tool configurations for Sim integrations. Your job is to read API documentation and create properly structured tool files.
## Your Task
When the user asks you to create tools for a service:
1. Use Context7 or WebFetch to read the service's API documentation
2. Create the tools directory structure
3. Generate properly typed tool configurations
## Directory Structure
Create files in `apps/sim/tools/{service}/`:
```
tools/{service}/
├── index.ts # Barrel export
├── types.ts # Parameter & response types
└── {action}.ts # Individual tool files (one per operation)
```
## Tool Configuration Structure
Every tool MUST follow this exact structure:
```typescript
import type { {ServiceName}{Action}Params } from '@/tools/{service}/types'
import type { ToolConfig } from '@/tools/types'
interface {ServiceName}{Action}Response {
success: boolean
output: {
// Define output structure here
}
}
export const {serviceName}{Action}Tool: ToolConfig<
{ServiceName}{Action}Params,
{ServiceName}{Action}Response
> = {
id: '{service}_{action}', // snake_case, matches tool name
name: '{Service} {Action}', // Human readable
description: 'Brief description', // One sentence
version: '1.0.0',
// OAuth config (if service uses OAuth)
oauth: {
required: true,
provider: '{service}', // Must match OAuth provider ID
},
params: {
// Hidden params (system-injected, only use hidden for oauth accessToken)
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
// User-only params (credentials, api key, IDs user must provide)
someId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The ID of the resource',
},
// User-or-LLM params (everything else, can be provided by user OR computed by LLM)
query: {
type: 'string',
required: false, // Use false for optional
visibility: 'user-or-llm',
description: 'Search query',
},
},
request: {
url: (params) => `https://api.service.com/v1/resource/${params.id}`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => ({
// Request body - only for POST/PUT/PATCH
// Trim ID fields to prevent copy-paste whitespace errors:
// userId: params.userId?.trim(),
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
// Map API response to output
// Use ?? null for nullable fields
// Use ?? [] for optional arrays
},
}
},
outputs: {
// Define each output field
},
}
```
## Critical Rules for Parameters
### Visibility Options
- `'hidden'` - System-injected (OAuth tokens, internal params). User never sees.
- `'user-only'` - User must provide (credentials, api keys, account-specific IDs)
- `'user-or-llm'` - User provides OR LLM can compute (search queries, content, filters, most fall into this category)
### Parameter Types
- `'string'` - Text values
- `'number'` - Numeric values
- `'boolean'` - True/false
- `'json'` - Complex objects (NOT 'object', use 'json')
- `'file'` - Single file
- `'file[]'` - Multiple files
### Required vs Optional
- Always explicitly set `required: true` or `required: false`
- Optional params should have `required: false`
## Critical Rules for Outputs
### Output Types
- `'string'`, `'number'`, `'boolean'` - Primitives
- `'json'` - Complex objects (use this, NOT 'object')
- `'array'` - Arrays with `items` property
- `'object'` - Objects with `properties` property
### Optional Outputs
Add `optional: true` for fields that may not exist in the response:
```typescript
closedAt: {
type: 'string',
description: 'When the issue was closed',
optional: true,
},
```
### Typed JSON Outputs
When using `type: 'json'` and you know the object shape in advance, **always define the inner structure** using `properties` so downstream consumers know what fields are available:
```typescript
// BAD: Opaque json with no info about what's inside
metadata: {
type: 'json',
description: 'Response metadata',
},
// GOOD: Define the known properties
metadata: {
type: 'json',
description: 'Response metadata',
properties: {
id: { type: 'string', description: 'Unique ID' },
status: { type: 'string', description: 'Current status' },
count: { type: 'number', description: 'Total count' },
},
},
```
For arrays of objects, define the item structure:
```typescript
items: {
type: 'array',
description: 'List of items',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Item ID' },
name: { type: 'string', description: 'Item name' },
},
},
},
```
Only use bare `type: 'json'` without `properties` when the shape is truly dynamic or unknown.
## Critical Rules for transformResponse
### Handle Nullable Fields
ALWAYS use `?? null` for fields that may be undefined:
```typescript
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
title: data.title,
body: data.body ?? null, // May be undefined
assignee: data.assignee ?? null, // May be undefined
labels: data.labels ?? [], // Default to empty array
closedAt: data.closed_at ?? null, // May be undefined
},
}
}
```
### Never Output Raw JSON Dumps
DON'T do this:
```typescript
output: {
data: data, // BAD - raw JSON dump
}
```
DO this instead - extract meaningful fields:
```typescript
output: {
id: data.id,
name: data.name,
status: data.status,
metadata: {
createdAt: data.created_at,
updatedAt: data.updated_at,
},
}
```
## Types File Pattern
Create `types.ts` with interfaces for all params and responses:
```typescript
import type { ToolResponse } from '@/tools/types'
// Parameter interfaces
export interface {Service}{Action}Params {
accessToken: string
requiredField: string
optionalField?: string
}
// Response interfaces (extend ToolResponse)
export interface {Service}{Action}Response extends ToolResponse {
output: {
field1: string
field2: number
optionalField?: string | null
}
}
```
## Index.ts Barrel Export Pattern
```typescript
// Export all tools
export { serviceTool1 } from './{action1}'
export { serviceTool2 } from './{action2}'
// Export types
export * from './types'
```
## Registering Tools
After creating tools, remind the user to:
1. Import tools in `apps/sim/tools/registry.ts`
2. Add to the `tools` object with snake_case keys:
```typescript
import { serviceActionTool } from '@/tools/{service}'
export const tools = {
// ... existing tools ...
{service}_{action}: serviceActionTool,
}
```
## V2 Tool Pattern
If creating V2 tools (API-aligned outputs), use `_v2` suffix:
- Tool ID: `{service}_{action}_v2`
- Variable name: `{action}V2Tool`
- Version: `'2.0.0'`
- Outputs: Flat, API-aligned (no content/metadata wrapper)
## Naming Convention
All tool IDs MUST use `snake_case`: `{service}_{action}` (e.g., `x_create_tweet`, `slack_send_message`). Never use camelCase or PascalCase for tool IDs.
## Checklist Before Finishing
- [ ] All tool IDs use snake_case
- [ ] All params have explicit `required: true` or `required: false`
- [ ] All params have appropriate `visibility`
- [ ] All nullable response fields use `?? null`
- [ ] All optional outputs have `optional: true`
- [ ] No raw JSON dumps in outputs
- [ ] Types file has all interfaces
- [ ] Index.ts exports all tools
## Final Validation (Required)
After creating all tools, you MUST validate every tool before finishing:
1. **Read every tool file** you created — do not skip any
2. **Cross-reference with the API docs** to verify:
- All required params are marked `required: true`
- All optional params are marked `required: false`
- Param types match the API (string, number, boolean, json)
- Request URL, method, headers, and body match the API spec
- `transformResponse` extracts the correct fields from the API response
- All output fields match what the API actually returns
- No fields are missing from outputs that the API provides
- No extra fields are defined in outputs that the API doesn't return
3. **Verify consistency** across tools:
- Shared types in `types.ts` match all tools that use them
- Tool IDs in the barrel export match the tool file definitions
- Error handling is consistent (error checks, meaningful messages)

View File

@@ -0,0 +1,708 @@
---
description: Create webhook triggers for a Sim integration using the generic trigger builder
argument-hint: <service-name>
---
# Add Trigger Skill
You are an expert at creating webhook triggers for Sim. You understand the trigger system, the generic `buildTriggerSubBlocks` helper, and how triggers connect to blocks.
## Your Task
When the user asks you to create triggers for a service:
1. Research what webhook events the service supports
2. Create the trigger files using the generic builder
3. Register triggers and connect them to the block
## Directory Structure
```
apps/sim/triggers/{service}/
├── index.ts # Barrel exports
├── utils.ts # Service-specific helpers (trigger options, setup instructions, extra fields)
├── {event_a}.ts # Primary trigger (includes dropdown)
├── {event_b}.ts # Secondary trigger (no dropdown)
├── {event_c}.ts # Secondary trigger (no dropdown)
└── webhook.ts # Generic webhook trigger (optional, for "all events")
```
## Step 1: Create utils.ts
This file contains service-specific helpers used by all triggers.
```typescript
import type { SubBlockConfig } from '@/blocks/types'
import type { TriggerOutput } from '@/triggers/types'
/**
* Dropdown options for the trigger type selector.
* These appear in the primary trigger's dropdown.
*/
export const {service}TriggerOptions = [
{ label: 'Event A', id: '{service}_event_a' },
{ label: 'Event B', id: '{service}_event_b' },
{ label: 'Event C', id: '{service}_event_c' },
{ label: 'Generic Webhook (All Events)', id: '{service}_webhook' },
]
/**
* Generates HTML setup instructions for the trigger.
* Displayed to users to help them configure webhooks in the external service.
*/
export function {service}SetupInstructions(eventType: string): string {
const instructions = [
'Copy the <strong>Webhook URL</strong> above',
'Go to <strong>{Service} Settings > Webhooks</strong>',
'Click <strong>Add Webhook</strong>',
'Paste the webhook URL',
`Select the <strong>${eventType}</strong> event type`,
'Save the webhook configuration',
'Click "Save" above to activate your trigger',
]
return instructions
.map((instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join('')
}
/**
* Service-specific extra fields to add to triggers.
* These are inserted between webhookUrl and triggerSave.
*/
export function build{Service}ExtraFields(triggerId: string): SubBlockConfig[] {
return [
{
id: 'projectId',
title: 'Project ID (Optional)',
type: 'short-input',
placeholder: 'Leave empty for all projects',
description: 'Optionally filter to a specific project',
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: triggerId },
},
]
}
/**
* Build outputs for this trigger type.
* Outputs define what data is available to downstream blocks.
*/
export function build{Service}Outputs(): Record<string, TriggerOutput> {
return {
eventType: { type: 'string', description: 'The type of event that triggered this workflow' },
resourceId: { type: 'string', description: 'ID of the affected resource' },
timestamp: { type: 'string', description: 'When the event occurred (ISO 8601)' },
// Nested outputs for complex data
resource: {
id: { type: 'string', description: 'Resource ID' },
name: { type: 'string', description: 'Resource name' },
status: { type: 'string', description: 'Current status' },
},
webhook: { type: 'json', description: 'Full webhook payload' },
}
}
```
## Step 2: Create the Primary Trigger
The **primary trigger** is the first one listed. It MUST include `includeDropdown: true` so users can switch between trigger types.
```typescript
import { {Service}Icon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
build{Service}ExtraFields,
build{Service}Outputs,
{service}SetupInstructions,
{service}TriggerOptions,
} from '@/triggers/{service}/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* {Service} Event A Trigger
*
* This is the PRIMARY trigger - it includes the dropdown for selecting trigger type.
*/
export const {service}EventATrigger: TriggerConfig = {
id: '{service}_event_a',
name: '{Service} Event A',
provider: '{service}',
description: 'Trigger workflow when Event A occurs',
version: '1.0.0',
icon: {Service}Icon,
subBlocks: buildTriggerSubBlocks({
triggerId: '{service}_event_a',
triggerOptions: {service}TriggerOptions,
includeDropdown: true, // PRIMARY TRIGGER - includes dropdown
setupInstructions: {service}SetupInstructions('Event A'),
extraFields: build{Service}ExtraFields('{service}_event_a'),
}),
outputs: build{Service}Outputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
```
## Step 3: Create Secondary Triggers
Secondary triggers do NOT include the dropdown (it's already in the primary trigger).
```typescript
import { {Service}Icon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
build{Service}ExtraFields,
build{Service}Outputs,
{service}SetupInstructions,
{service}TriggerOptions,
} from '@/triggers/{service}/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* {Service} Event B Trigger
*/
export const {service}EventBTrigger: TriggerConfig = {
id: '{service}_event_b',
name: '{Service} Event B',
provider: '{service}',
description: 'Trigger workflow when Event B occurs',
version: '1.0.0',
icon: {Service}Icon,
subBlocks: buildTriggerSubBlocks({
triggerId: '{service}_event_b',
triggerOptions: {service}TriggerOptions,
// NO includeDropdown - secondary trigger
setupInstructions: {service}SetupInstructions('Event B'),
extraFields: build{Service}ExtraFields('{service}_event_b'),
}),
outputs: build{Service}Outputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
```
## Step 4: Create index.ts Barrel Export
```typescript
export { {service}EventATrigger } from './event_a'
export { {service}EventBTrigger } from './event_b'
export { {service}EventCTrigger } from './event_c'
export { {service}WebhookTrigger } from './webhook'
```
## Step 5: Register Triggers
### Trigger Registry (`apps/sim/triggers/registry.ts`)
```typescript
// Add import
import {
{service}EventATrigger,
{service}EventBTrigger,
{service}EventCTrigger,
{service}WebhookTrigger,
} from '@/triggers/{service}'
// Add to TRIGGER_REGISTRY
export const TRIGGER_REGISTRY: TriggerRegistry = {
// ... existing triggers ...
{service}_event_a: {service}EventATrigger,
{service}_event_b: {service}EventBTrigger,
{service}_event_c: {service}EventCTrigger,
{service}_webhook: {service}WebhookTrigger,
}
```
## Step 6: Connect Triggers to Block
In the block file (`apps/sim/blocks/blocks/{service}.ts`):
```typescript
import { {Service}Icon } from '@/components/icons'
import { getTrigger } from '@/triggers'
import type { BlockConfig } from '@/blocks/types'
export const {Service}Block: BlockConfig = {
type: '{service}',
name: '{Service}',
// ... other config ...
// Enable triggers and list available trigger IDs
triggers: {
enabled: true,
available: [
'{service}_event_a',
'{service}_event_b',
'{service}_event_c',
'{service}_webhook',
],
},
subBlocks: [
// Regular tool subBlocks first
{ id: 'operation', /* ... */ },
{ id: 'credential', /* ... */ },
// ... other tool fields ...
// Then spread ALL trigger subBlocks
...getTrigger('{service}_event_a').subBlocks,
...getTrigger('{service}_event_b').subBlocks,
...getTrigger('{service}_event_c').subBlocks,
...getTrigger('{service}_webhook').subBlocks,
],
// ... tools config ...
}
```
## Automatic Webhook Registration (Preferred)
If the service's API supports programmatic webhook creation, implement automatic webhook registration instead of requiring users to manually configure webhooks. This provides a much better user experience.
### When to Use Automatic Registration
Check the service's API documentation for endpoints like:
- `POST /webhooks` or `POST /hooks` - Create webhook
- `DELETE /webhooks/{id}` - Delete webhook
Services that support this pattern include: Grain, Lemlist, Calendly, Airtable, Webflow, Typeform, etc.
### Implementation Steps
#### 1. Add API Key to Extra Fields
Update your `build{Service}ExtraFields` function to include an API key field:
```typescript
export function build{Service}ExtraFields(triggerId: string): SubBlockConfig[] {
return [
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Enter your {Service} API key',
description: 'Required to create the webhook in {Service}.',
password: true,
required: true,
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: triggerId },
},
// Other optional fields (e.g., campaign filter, project filter)
{
id: 'projectId',
title: 'Project ID (Optional)',
type: 'short-input',
placeholder: 'Leave empty for all projects',
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: triggerId },
},
]
}
```
#### 2. Update Setup Instructions for Automatic Creation
Change instructions to indicate automatic webhook creation:
```typescript
export function {service}SetupInstructions(eventType: string): string {
const instructions = [
'Enter your {Service} API Key above.',
'You can find your API key in {Service} at <strong>Settings > API</strong>.',
`Click <strong>"Save Configuration"</strong> to automatically create the webhook in {Service} for <strong>${eventType}</strong> events.`,
'The webhook will be automatically deleted when you remove this trigger.',
]
return instructions
.map((instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join('')
}
```
#### 3. Add Webhook Creation to API Route
In `apps/sim/app/api/webhooks/route.ts`, add provider-specific logic after the database save:
```typescript
// --- {Service} specific logic ---
if (savedWebhook && provider === '{service}') {
logger.info(`[${requestId}] {Service} provider detected. Creating webhook subscription.`)
try {
const result = await create{Service}WebhookSubscription(
{
id: savedWebhook.id,
path: savedWebhook.path,
providerConfig: savedWebhook.providerConfig,
},
requestId
)
if (result) {
// Update the webhook record with the external webhook ID
const updatedConfig = {
...(savedWebhook.providerConfig as Record<string, any>),
externalId: result.id,
}
await db
.update(webhook)
.set({
providerConfig: updatedConfig,
updatedAt: new Date(),
})
.where(eq(webhook.id, savedWebhook.id))
savedWebhook.providerConfig = updatedConfig
logger.info(`[${requestId}] Successfully created {Service} webhook`, {
externalHookId: result.id,
webhookId: savedWebhook.id,
})
}
} catch (err) {
logger.error(
`[${requestId}] Error creating {Service} webhook subscription, rolling back webhook`,
err
)
await db.delete(webhook).where(eq(webhook.id, savedWebhook.id))
return NextResponse.json(
{
error: 'Failed to create webhook in {Service}',
details: err instanceof Error ? err.message : 'Unknown error',
},
{ status: 500 }
)
}
}
// --- End {Service} specific logic ---
```
Then add the helper function at the end of the file:
```typescript
async function create{Service}WebhookSubscription(
webhookData: any,
requestId: string
): Promise<{ id: string } | undefined> {
try {
const { path, providerConfig } = webhookData
const { apiKey, triggerId, projectId } = providerConfig || {}
if (!apiKey) {
throw new Error('{Service} API Key is required.')
}
// Map trigger IDs to service event types
const eventTypeMap: Record<string, string | undefined> = {
{service}_event_a: 'eventA',
{service}_event_b: 'eventB',
{service}_webhook: undefined, // Generic - no filter
}
const eventType = eventTypeMap[triggerId]
const notificationUrl = `${getBaseUrl()}/api/webhooks/trigger/${path}`
const requestBody: Record<string, any> = {
url: notificationUrl,
}
if (eventType) {
requestBody.eventType = eventType
}
if (projectId) {
requestBody.projectId = projectId
}
const response = await fetch('https://api.{service}.com/webhooks', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
})
const responseBody = await response.json()
if (!response.ok) {
const errorMessage = responseBody.message || 'Unknown API error'
let userFriendlyMessage = 'Failed to create webhook in {Service}'
if (response.status === 401) {
userFriendlyMessage = 'Invalid API Key. Please verify and try again.'
} else if (errorMessage) {
userFriendlyMessage = `{Service} error: ${errorMessage}`
}
throw new Error(userFriendlyMessage)
}
return { id: responseBody.id }
} catch (error: any) {
logger.error(`Exception during {Service} webhook creation`, { error: error.message })
throw error
}
}
```
#### 4. Add Webhook Deletion to Provider Subscriptions
In `apps/sim/lib/webhooks/provider-subscriptions.ts`:
1. Add a logger:
```typescript
const {service}Logger = createLogger('{Service}Webhook')
```
2. Add the delete function:
```typescript
export async function delete{Service}Webhook(webhook: any, requestId: string): Promise<void> {
try {
const config = getProviderConfig(webhook)
const apiKey = config.apiKey as string | undefined
const externalId = config.externalId as string | undefined
if (!apiKey || !externalId) {
{service}Logger.warn(`[${requestId}] Missing apiKey or externalId, skipping cleanup`)
return
}
const response = await fetch(`https://api.{service}.com/webhooks/${externalId}`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${apiKey}`,
},
})
if (!response.ok && response.status !== 404) {
{service}Logger.warn(`[${requestId}] Failed to delete webhook (non-fatal): ${response.status}`)
} else {
{service}Logger.info(`[${requestId}] Successfully deleted webhook ${externalId}`)
}
} catch (error) {
{service}Logger.warn(`[${requestId}] Error deleting webhook (non-fatal)`, error)
}
}
```
3. Add to `cleanupExternalWebhook`:
```typescript
export async function cleanupExternalWebhook(...): Promise<void> {
// ... existing providers ...
} else if (webhook.provider === '{service}') {
await delete{Service}Webhook(webhook, requestId)
}
}
```
### Key Points for Automatic Registration
- **API Key visibility**: Always use `password: true` for API key fields
- **Error handling**: Roll back the database webhook if external creation fails
- **External ID storage**: Save the external webhook ID in `providerConfig.externalId`
- **Graceful cleanup**: Don't fail webhook deletion if cleanup fails (use non-fatal logging)
- **User-friendly errors**: Map HTTP status codes to helpful error messages
## The buildTriggerSubBlocks Helper
This is the generic helper from `@/triggers` that creates consistent trigger subBlocks.
### Function Signature
```typescript
interface BuildTriggerSubBlocksOptions {
triggerId: string // e.g., 'service_event_a'
triggerOptions: Array<{ label: string; id: string }> // Dropdown options
includeDropdown?: boolean // true only for primary trigger
setupInstructions: string // HTML instructions
extraFields?: SubBlockConfig[] // Service-specific fields
webhookPlaceholder?: string // Custom placeholder text
}
function buildTriggerSubBlocks(options: BuildTriggerSubBlocksOptions): SubBlockConfig[]
```
### What It Creates
The helper creates this structure:
1. **Dropdown** (only if `includeDropdown: true`) - Trigger type selector
2. **Webhook URL** - Read-only field with copy button
3. **Extra Fields** - Your service-specific fields (filters, options, etc.)
4. **Save Button** - Activates the trigger
5. **Instructions** - Setup guide for users
All fields automatically have:
- `mode: 'trigger'` - Only shown in trigger mode
- `condition: { field: 'selectedTriggerId', value: triggerId }` - Only shown when this trigger is selected
## Trigger Outputs & Webhook Input Formatting
### Important: Two Sources of Truth
There are two related but separate concerns:
1. **Trigger `outputs`** - Schema/contract defining what fields SHOULD be available. Used by UI for tag dropdown.
2. **`formatWebhookInput`** - Implementation that transforms raw webhook payload into actual data. Located in `apps/sim/lib/webhooks/utils.server.ts`.
**These MUST be aligned.** The fields returned by `formatWebhookInput` should match what's defined in trigger `outputs`. If they differ:
- Tag dropdown shows fields that don't exist (broken variable resolution)
- Or actual data has fields not shown in dropdown (users can't discover them)
### When to Add a formatWebhookInput Handler
- **Simple providers**: If the raw webhook payload structure already matches your outputs, you don't need a handler. The generic fallback returns `body` directly.
- **Complex providers**: If you need to transform, flatten, extract nested data, compute fields, or handle conditional logic, add a handler.
### Adding a Handler
In `apps/sim/lib/webhooks/utils.server.ts`, add a handler block:
```typescript
if (foundWebhook.provider === '{service}') {
// Transform raw webhook body to match trigger outputs
return {
eventType: body.type,
resourceId: body.data?.id || '',
timestamp: body.created_at,
resource: body.data,
}
}
```
**Key rules:**
- Return fields that match your trigger `outputs` definition exactly
- No wrapper objects like `webhook: { data: ... }` or `{service}: { ... }`
- No duplication (don't spread body AND add individual fields)
- Use `null` for missing optional data, not empty objects with empty strings
### Verify Alignment
Run the alignment checker:
```bash
bunx scripts/check-trigger-alignment.ts {service}
```
## Trigger Outputs
Trigger outputs use the same schema as block outputs (NOT tool outputs).
**Supported:**
- `type` and `description` for simple fields
- Nested object structure for complex data
**NOT Supported:**
- `optional: true` (tool outputs only)
- `items` property (tool outputs only)
```typescript
export function buildOutputs(): Record<string, TriggerOutput> {
return {
// Simple fields
eventType: { type: 'string', description: 'Event type' },
timestamp: { type: 'string', description: 'When it occurred' },
// Complex data - use type: 'json'
payload: { type: 'json', description: 'Full event payload' },
// Nested structure
resource: {
id: { type: 'string', description: 'Resource ID' },
name: { type: 'string', description: 'Resource name' },
},
}
}
```
## Generic Webhook Trigger Pattern
For services with many event types, create a generic webhook that accepts all events:
```typescript
export const {service}WebhookTrigger: TriggerConfig = {
id: '{service}_webhook',
name: '{Service} Webhook (All Events)',
// ...
subBlocks: buildTriggerSubBlocks({
triggerId: '{service}_webhook',
triggerOptions: {service}TriggerOptions,
setupInstructions: {service}SetupInstructions('All Events'),
extraFields: [
// Event type filter (optional)
{
id: 'eventTypes',
title: 'Event Types',
type: 'dropdown',
multiSelect: true,
options: [
{ label: 'Event A', id: 'event_a' },
{ label: 'Event B', id: 'event_b' },
],
placeholder: 'Leave empty for all events',
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: '{service}_webhook' },
},
// Plus any other service-specific fields
...build{Service}ExtraFields('{service}_webhook'),
],
}),
}
```
## Checklist Before Finishing
### Utils
- [ ] Created `{service}TriggerOptions` array with all trigger IDs
- [ ] Created `{service}SetupInstructions` function with clear steps
- [ ] Created `build{Service}ExtraFields` for service-specific fields
- [ ] Created output builders for each trigger type
### Triggers
- [ ] Primary trigger has `includeDropdown: true`
- [ ] Secondary triggers do NOT have `includeDropdown`
- [ ] All triggers use `buildTriggerSubBlocks` helper
- [ ] All triggers have proper outputs defined
- [ ] Created `index.ts` barrel export
### Registration
- [ ] All triggers imported in `triggers/registry.ts`
- [ ] All triggers added to `TRIGGER_REGISTRY`
- [ ] Block has `triggers.enabled: true`
- [ ] Block has all trigger IDs in `triggers.available`
- [ ] Block spreads all trigger subBlocks: `...getTrigger('id').subBlocks`
### Automatic Webhook Registration (if supported)
- [ ] Added API key field to `build{Service}ExtraFields` with `password: true`
- [ ] Updated setup instructions for automatic webhook creation
- [ ] Added provider-specific logic to `apps/sim/app/api/webhooks/route.ts`
- [ ] Added `create{Service}WebhookSubscription` helper function
- [ ] Added `delete{Service}Webhook` function to `provider-subscriptions.ts`
- [ ] Added provider to `cleanupExternalWebhook` function
### Webhook Input Formatting
- [ ] Added handler in `apps/sim/lib/webhooks/utils.server.ts` (if custom formatting needed)
- [ ] Handler returns fields matching trigger `outputs` exactly
- [ ] Run `bunx scripts/check-trigger-alignment.ts {service}` to verify alignment
### Testing
- [ ] Run `bun run type-check` to verify no TypeScript errors
- [ ] Restart dev server to pick up new triggers
- [ ] Test trigger UI shows correctly in the block
- [ ] Test automatic webhook creation works (if applicable)

View File

@@ -0,0 +1,289 @@
---
description: Validate an existing Sim integration (tools, block, registry) against the service's API docs
argument-hint: <service-name> [api-docs-url]
---
# Validate Integration Skill
You are an expert auditor for Sim integrations. Your job is to thoroughly validate that an existing integration is correct, complete, and follows all conventions.
## Your Task
When the user asks you to validate an integration:
1. Read the service's API documentation (via WebFetch or Context7)
2. Read every tool, the block, and registry entries
3. Cross-reference everything against the API docs and Sim conventions
4. Report all issues found, grouped by severity (critical, warning, suggestion)
5. Fix all issues after reporting them
## Step 1: Gather All Files
Read **every** file for the integration — do not skip any:
```
apps/sim/tools/{service}/ # All tool files, types.ts, index.ts
apps/sim/blocks/blocks/{service}.ts # Block definition
apps/sim/tools/registry.ts # Tool registry entries for this service
apps/sim/blocks/registry.ts # Block registry entry for this service
apps/sim/components/icons.tsx # Icon definition
apps/sim/lib/auth/auth.ts # OAuth config — should use getCanonicalScopesForProvider()
apps/sim/lib/oauth/oauth.ts # OAuth provider config — single source of truth for scopes
apps/sim/lib/oauth/utils.ts # Scope utilities, SCOPE_DESCRIPTIONS for modal UI
```
## Step 2: Pull API Documentation
Fetch the official API docs for the service. This is the **source of truth** for:
- Endpoint URLs, HTTP methods, and auth headers
- Required vs optional parameters
- Parameter types and allowed values
- Response shapes and field names
- Pagination patterns (which param name, which response field)
- Rate limits and error formats
## Step 3: Validate Tools
For **every** tool file, check:
### Tool ID and Naming
- [ ] Tool ID uses `snake_case`: `{service}_{action}` (e.g., `x_create_tweet`, `slack_send_message`)
- [ ] Tool `name` is human-readable (e.g., `'X Create Tweet'`)
- [ ] Tool `description` is a concise one-liner describing what it does
- [ ] Tool `version` is set (`'1.0.0'` or `'2.0.0'` for V2)
### Params
- [ ] All required API params are marked `required: true`
- [ ] All optional API params are marked `required: false`
- [ ] Every param has explicit `required: true` or `required: false` — never omitted
- [ ] Param types match the API (`'string'`, `'number'`, `'boolean'`, `'json'`)
- [ ] Visibility is correct:
- `'hidden'` — ONLY for OAuth access tokens and system-injected params
- `'user-only'` — for API keys, credentials, and account-specific IDs the user must provide
- `'user-or-llm'` — for everything else (search queries, content, filters, IDs that could come from other blocks)
- [ ] Every param has a `description` that explains what it does
### Request
- [ ] URL matches the API endpoint exactly (correct base URL, path segments, path params)
- [ ] HTTP method matches the API spec (GET, POST, PUT, PATCH, DELETE)
- [ ] Headers include correct auth pattern:
- OAuth: `Authorization: Bearer ${params.accessToken}`
- API Key: correct header name and format per the service's docs
- [ ] `Content-Type` header is set for POST/PUT/PATCH requests
- [ ] Body sends all required fields and only includes optional fields when provided
- [ ] For GET requests with query params: URL is constructed correctly with query string
- [ ] ID fields in URL paths are `.trim()`-ed to prevent copy-paste whitespace errors
- [ ] Path params use template literals correctly: `` `https://api.service.com/v1/${params.id.trim()}` ``
### Response / transformResponse
- [ ] Correctly parses the API response (`await response.json()`)
- [ ] Extracts the right fields from the response structure (e.g., `data.data` vs `data` vs `data.results`)
- [ ] All nullable fields use `?? null`
- [ ] All optional arrays use `?? []`
- [ ] Error cases are handled: checks for missing/empty data and returns meaningful error
- [ ] Does NOT do raw JSON dumps — extracts meaningful, individual fields
### Outputs
- [ ] All output fields match what the API actually returns
- [ ] No fields are missing that the API provides and users would commonly need
- [ ] No phantom fields defined that the API doesn't return
- [ ] `optional: true` is set on fields that may not exist in all responses
- [ ] When using `type: 'json'` and the shape is known, `properties` defines the inner fields
- [ ] When using `type: 'array'`, `items` defines the item structure with `properties`
- [ ] Field descriptions are accurate and helpful
### Types (types.ts)
- [ ] Has param interfaces for every tool (e.g., `XCreateTweetParams`)
- [ ] Has response interfaces for every tool (extending `ToolResponse`)
- [ ] Optional params use `?` in the interface (e.g., `replyTo?: string`)
- [ ] Field names in types match actual API field names
- [ ] Shared response types are properly reused (e.g., `XTweetResponse` shared across tweet tools)
### Barrel Export (index.ts)
- [ ] Every tool is exported
- [ ] All types are re-exported (`export * from './types'`)
- [ ] No orphaned exports (tools that don't exist)
### Tool Registry (tools/registry.ts)
- [ ] Every tool is imported and registered
- [ ] Registry keys use snake_case and match tool IDs exactly
- [ ] Entries are in alphabetical order within the file
## Step 4: Validate Block
### Block ↔ Tool Alignment (CRITICAL)
This is the most important validation — the block must be perfectly aligned with every tool it references.
For **each tool** in `tools.access`:
- [ ] The operation dropdown has an option whose ID matches the tool ID (or the `tools.config.tool` function correctly maps to it)
- [ ] Every **required** tool param (except `accessToken`) has a corresponding subBlock input that is:
- Shown when that operation is selected (correct `condition`)
- Marked as `required: true` (or conditionally required)
- [ ] Every **optional** tool param has a corresponding subBlock input (or is intentionally omitted if truly never needed)
- [ ] SubBlock `id` values are unique across the entire block — no duplicates even across different conditions
- [ ] The `tools.config.tool` function returns the correct tool ID for every possible operation value
- [ ] The `tools.config.params` function correctly maps subBlock IDs to tool param names when they differ
### SubBlocks
- [ ] Operation dropdown lists ALL tool operations available in `tools.access`
- [ ] Dropdown option labels are human-readable and descriptive
- [ ] Conditions use correct syntax:
- Single value: `{ field: 'operation', value: 'x_create_tweet' }`
- Multiple values (OR): `{ field: 'operation', value: ['x_create_tweet', 'x_delete_tweet'] }`
- Negation: `{ field: 'operation', value: 'delete', not: true }`
- Compound: `{ field: 'op', value: 'send', and: { field: 'type', value: 'dm' } }`
- [ ] Condition arrays include ALL operations that use that field — none missing
- [ ] `dependsOn` is set for fields that need other values (selectors depending on credential, cascading dropdowns)
- [ ] SubBlock types match tool param types:
- Enum/fixed options → `dropdown`
- Free text → `short-input`
- Long text/content → `long-input`
- True/false → `dropdown` with Yes/No options (not `switch` unless purely UI toggle)
- Credentials → `oauth-input` with correct `serviceId`
- [ ] Dropdown `value: () => 'default'` is set for dropdowns with a sensible default
### Advanced Mode
- [ ] Optional, rarely-used fields are set to `mode: 'advanced'`:
- Pagination tokens / next tokens
- Time range filters (start/end time)
- Sort order / direction options
- Max results / per page limits
- Reply settings / threading options
- Rarely used IDs (reply-to, quote-tweet, etc.)
- Exclude filters
- [ ] **Required** fields are NEVER set to `mode: 'advanced'`
- [ ] Fields that users fill in most of the time are NOT set to `mode: 'advanced'`
### WandConfig
- [ ] Timestamp fields have `wandConfig` with `generationType: 'timestamp'`
- [ ] Comma-separated list fields have `wandConfig` with a descriptive prompt
- [ ] Complex filter/query fields have `wandConfig` with format examples in the prompt
- [ ] All `wandConfig` prompts end with "Return ONLY the [format] - no explanations, no extra text."
- [ ] `wandConfig.placeholder` describes what to type in natural language
### Tools Config
- [ ] `tools.access` lists **every** tool ID the block can use — none missing
- [ ] `tools.config.tool` returns the correct tool ID for each operation
- [ ] Type coercions are in `tools.config.params` (runs at execution time), NOT in `tools.config.tool` (runs at serialization time before variable resolution)
- [ ] `tools.config.params` handles:
- `Number()` conversion for numeric params that come as strings from inputs
- `Boolean` / string-to-boolean conversion for toggle params
- Empty string → `undefined` conversion for optional dropdown values
- Any subBlock ID → tool param name remapping
- [ ] No `Number()`, `JSON.parse()`, or other coercions in `tools.config.tool` — these would destroy dynamic references like `<Block.output>`
### Block Outputs
- [ ] Outputs cover the key fields returned by ALL tools (not just one operation)
- [ ] Output types are correct (`'string'`, `'number'`, `'boolean'`, `'json'`)
- [ ] `type: 'json'` outputs either:
- Describe inner fields in the description string (GOOD): `'User profile (id, name, username, bio)'`
- Use nested output definitions (BEST): `{ id: { type: 'string' }, name: { type: 'string' } }`
- [ ] No opaque `type: 'json'` with vague descriptions like `'Response data'`
- [ ] Outputs that only appear for certain operations use `condition` if supported, or document which operations return them
### Block Metadata
- [ ] `type` is snake_case (e.g., `'x'`, `'cloudflare'`)
- [ ] `name` is human-readable (e.g., `'X'`, `'Cloudflare'`)
- [ ] `description` is a concise one-liner
- [ ] `longDescription` provides detail for docs
- [ ] `docsLink` points to `'https://docs.sim.ai/tools/{service}'`
- [ ] `category` is `'tools'`
- [ ] `bgColor` uses the service's brand color hex
- [ ] `icon` references the correct icon component from `@/components/icons`
- [ ] `authMode` is set correctly (`AuthMode.OAuth` or `AuthMode.ApiKey`)
- [ ] Block is registered in `blocks/registry.ts` alphabetically
### Block Inputs
- [ ] `inputs` section lists all subBlock params that the block accepts
- [ ] Input types match the subBlock types
- [ ] When using `canonicalParamId`, inputs list the canonical ID (not the raw subBlock IDs)
## Step 5: Validate OAuth Scopes (if OAuth service)
Scopes are centralized — the single source of truth is `OAUTH_PROVIDERS` in `lib/oauth/oauth.ts`.
- [ ] Scopes defined in `lib/oauth/oauth.ts` under `OAUTH_PROVIDERS[provider].services[service].scopes`
- [ ] `auth.ts` uses `getCanonicalScopesForProvider(providerId)` — NOT a hardcoded array
- [ ] Block `requiredScopes` uses `getScopesForService(serviceId)` — NOT a hardcoded array
- [ ] No hardcoded scope arrays in `auth.ts` or block files (should all use utility functions)
- [ ] Each scope has a human-readable description in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`
- [ ] No excess scopes that aren't needed by any tool
## Step 6: Validate Pagination Consistency
If any tools support pagination:
- [ ] Pagination param names match the API docs (e.g., `pagination_token` vs `next_token` vs `cursor`)
- [ ] Different API endpoints that use different pagination param names have separate subBlocks in the block
- [ ] Pagination response fields (`nextToken`, `cursor`, etc.) are included in tool outputs
- [ ] Pagination subBlocks are set to `mode: 'advanced'`
## Step 7: Validate Error Handling
- [ ] `transformResponse` checks for error conditions before accessing data
- [ ] Error responses include meaningful messages (not just generic "failed")
- [ ] HTTP error status codes are handled (check `response.ok` or status codes)
## Step 8: Report and Fix
### Report Format
Group findings by severity:
**Critical** (will cause runtime errors or incorrect behavior):
- Wrong endpoint URL or HTTP method
- Missing required params or wrong `required` flag
- Incorrect response field mapping (accessing wrong path in response)
- Missing error handling that would cause crashes
- Tool ID mismatch between tool file, registry, and block `tools.access`
- OAuth scopes missing in `auth.ts` that tools need
- `tools.config.tool` returning wrong tool ID for an operation
- Type coercions in `tools.config.tool` instead of `tools.config.params`
**Warning** (follows conventions incorrectly or has usability issues):
- Optional field not set to `mode: 'advanced'`
- Missing `wandConfig` on timestamp/complex fields
- Wrong `visibility` on params (e.g., `'hidden'` instead of `'user-or-llm'`)
- Missing `optional: true` on nullable outputs
- Opaque `type: 'json'` without property descriptions
- Missing `.trim()` on ID fields in request URLs
- Missing `?? null` on nullable response fields
- Block condition array missing an operation that uses that field
- Hardcoded scope arrays instead of using `getScopesForService()` / `getCanonicalScopesForProvider()`
- Missing scope description in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`
**Suggestion** (minor improvements):
- Better description text
- Inconsistent naming across tools
- Missing `longDescription` or `docsLink`
- Pagination fields that could benefit from `wandConfig`
### Fix All Issues
After reporting, fix every **critical** and **warning** issue. Apply **suggestions** where they don't add unnecessary complexity.
### Validation Output
After fixing, confirm:
1. `bun run lint` passes with no fixes needed
2. TypeScript compiles clean (no type errors)
3. Re-read all modified files to verify fixes are correct
## Checklist Summary
- [ ] Read ALL tool files, block, types, index, and registries
- [ ] Pulled and read official API documentation
- [ ] Validated every tool's ID, params, request, response, outputs, and types against API docs
- [ ] Validated block ↔ tool alignment (every tool param has a subBlock, every condition is correct)
- [ ] Validated advanced mode on optional/rarely-used fields
- [ ] Validated wandConfig on timestamps and complex inputs
- [ ] Validated tools.config mapping, tool selector, and type coercions
- [ ] Validated block outputs match what tools return, with typed JSON where possible
- [ ] Validated OAuth scopes use centralized utilities (getScopesForService, getCanonicalScopesForProvider) — no hardcoded arrays
- [ ] Validated scope descriptions exist in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts` for all scopes
- [ ] Validated pagination consistency across tools and block
- [ ] Validated error handling (error checks, meaningful messages)
- [ ] Validated registry entries (tools and block, alphabetical, correct imports)
- [ ] Reported all issues grouped by severity
- [ ] Fixed all critical and warning issues
- [ ] Ran `bun run lint` after fixes
- [ ] Verified TypeScript compiles clean

View File

@@ -0,0 +1,35 @@
---
paths:
- "apps/sim/components/emcn/**"
---
# EMCN Components
Import from `@/components/emcn`, never from subpaths (except CSS files).
## CVA vs Direct Styles
**Use CVA when:** 2+ variants (primary/secondary, sm/md/lg)
```tsx
const buttonVariants = cva('base-classes', {
variants: { variant: { default: '...', primary: '...' } }
})
export { Button, buttonVariants }
```
**Use direct className when:** Single consistent style, no variations
```tsx
function Label({ className, ...props }) {
return <Primitive className={cn('style-classes', className)} {...props} />
}
```
## Rules
- Use Radix UI primitives for accessibility
- Export component and variants (if using CVA)
- TSDoc with usage examples
- Consistent tokens: `font-medium`, `text-[12px]`, `rounded-[4px]`
- `transition-colors` for hover states

13
.claude/rules/global.md Normal file
View File

@@ -0,0 +1,13 @@
# Global Standards
## Logging
Import `createLogger` from `sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`.
## Comments
Use TSDoc for documentation. No `====` separators. No non-TSDoc comments.
## Styling
Never update global styles. Keep all styling local to components.
## Package Manager
Use `bun` and `bunx`, not `npm` and `npx`.

View File

@@ -0,0 +1,56 @@
---
paths:
- "apps/sim/**"
---
# Sim App Architecture
## Core Principles
1. **Single Responsibility**: Each component, hook, store has one clear purpose
2. **Composition Over Complexity**: Break down complex logic into smaller pieces
3. **Type Safety First**: TypeScript interfaces for all props, state, return types
4. **Predictable State**: Zustand for global state, useState for UI-only concerns
## Root-Level Structure
```
apps/sim/
├── app/ # Next.js app router (pages, API routes)
├── blocks/ # Block definitions and registry
├── components/ # Shared UI (emcn/, ui/)
├── executor/ # Workflow execution engine
├── hooks/ # Shared hooks (queries/, selectors/)
├── lib/ # App-wide utilities
├── providers/ # LLM provider integrations
├── stores/ # Zustand stores
├── tools/ # Tool definitions
└── triggers/ # Trigger definitions
```
## Feature Organization
Features live under `app/workspace/[workspaceId]/`:
```
feature/
├── components/ # Feature components
├── hooks/ # Feature-scoped hooks
├── utils/ # Feature-scoped utilities (2+ consumers)
├── feature.tsx # Main component
└── page.tsx # Next.js page entry
```
## Naming Conventions
- **Components**: PascalCase (`WorkflowList`)
- **Hooks**: `use` prefix (`useWorkflowOperations`)
- **Files**: kebab-case (`workflow-list.tsx`)
- **Stores**: `stores/feature/store.ts`
- **Constants**: SCREAMING_SNAKE_CASE
- **Interfaces**: PascalCase with suffix (`WorkflowListProps`)
## Utils Rules
- **Never create `utils.ts` for single consumer** - inline it
- **Create `utils.ts` when** 2+ files need the same helper
- **Check existing sources** before duplicating (`lib/` has many utilities)
- **Location**: `lib/` (app-wide) → `feature/utils/` (feature-scoped) → inline (single-use)

View File

@@ -0,0 +1,48 @@
---
paths:
- "apps/sim/**/*.tsx"
---
# Component Patterns
## Structure Order
```typescript
'use client' // Only if using hooks
// Imports (external → internal)
// Constants at module level
const CONFIG = { SPACING: 8 } as const
// Props interface
interface ComponentProps {
requiredProp: string
optionalProp?: boolean
}
export function Component({ requiredProp, optionalProp = false }: ComponentProps) {
// a. Refs
// b. External hooks (useParams, useRouter)
// c. Store hooks
// d. Custom hooks
// e. Local state
// f. useMemo
// g. useCallback
// h. useEffect
// i. Return JSX
}
```
## Rules
1. `'use client'` only when using React hooks
2. Always define props interface
3. Extract constants with `as const`
4. Semantic HTML (`aside`, `nav`, `article`)
5. Optional chain callbacks: `onAction?.(id)`
## Component Extraction
**Extract when:** 50+ lines, used in 2+ files, or has own state/logic
**Keep inline when:** < 10 lines, single use, purely presentational

View File

@@ -0,0 +1,55 @@
---
paths:
- "apps/sim/**/use-*.ts"
- "apps/sim/**/hooks/**/*.ts"
---
# Hook Patterns
## Structure
```typescript
interface UseFeatureProps {
id: string
onSuccess?: (result: Result) => void
}
export function useFeature({ id, onSuccess }: UseFeatureProps) {
// 1. Refs for stable dependencies
const idRef = useRef(id)
const onSuccessRef = useRef(onSuccess)
// 2. State
const [data, setData] = useState<Data | null>(null)
const [isLoading, setIsLoading] = useState(false)
// 3. Sync refs
useEffect(() => {
idRef.current = id
onSuccessRef.current = onSuccess
}, [id, onSuccess])
// 4. Operations (useCallback with empty deps when using refs)
const fetchData = useCallback(async () => {
setIsLoading(true)
try {
const result = await fetch(`/api/${idRef.current}`).then(r => r.json())
setData(result)
onSuccessRef.current?.(result)
} finally {
setIsLoading(false)
}
}, [])
return { data, isLoading, fetchData }
}
```
## Rules
1. Single responsibility per hook
2. Props interface required
3. Refs for stable callback dependencies
4. Wrap returned functions in useCallback
5. Always try/catch async operations
6. Track loading/error states

View File

@@ -0,0 +1,62 @@
---
paths:
- "apps/sim/**/*.ts"
- "apps/sim/**/*.tsx"
---
# Import Patterns
## Absolute Imports
**Always use absolute imports.** Never use relative imports.
```typescript
// ✓ Good
import { useWorkflowStore } from '@/stores/workflows/store'
import { Button } from '@/components/ui/button'
// ✗ Bad
import { useWorkflowStore } from '../../../stores/workflows/store'
```
## Barrel Exports
Use barrel exports (`index.ts`) when a folder has 3+ exports. Import from barrel, not individual files.
```typescript
// ✓ Good
import { Dashboard, Sidebar } from '@/app/workspace/[workspaceId]/logs/components'
// ✗ Bad
import { Dashboard } from '@/app/workspace/[workspaceId]/logs/components/dashboard/dashboard'
```
## No Re-exports
Do not re-export from non-barrel files. Import directly from the source.
```typescript
// ✓ Good - import from where it's declared
import { CORE_TRIGGER_TYPES } from '@/stores/logs/filters/types'
// ✗ Bad - re-exporting in utils.ts then importing from there
import { CORE_TRIGGER_TYPES } from '@/app/workspace/.../utils'
```
## Import Order
1. React/core libraries
2. External libraries
3. UI components (`@/components/emcn`, `@/components/ui`)
4. Utilities (`@/lib/...`)
5. Stores (`@/stores/...`)
6. Feature imports
7. CSS imports
## Type Imports
Use `type` keyword for type-only imports:
```typescript
import type { WorkflowLog } from '@/stores/logs/types'
```

View File

@@ -0,0 +1,287 @@
---
paths:
- "apps/sim/tools/**"
- "apps/sim/blocks/**"
- "apps/sim/triggers/**"
---
# Adding Integrations
## Overview
Adding a new integration typically requires:
1. **Tools** - API operations (`tools/{service}/`)
2. **Block** - UI component (`blocks/blocks/{service}.ts`)
3. **Icon** - SVG icon (`components/icons.tsx`)
4. **Trigger** (optional) - Webhooks/polling (`triggers/{service}/`)
Always look up the service's API docs first.
## 1. Tools (`tools/{service}/`)
```
tools/{service}/
├── index.ts # Export all tools
├── types.ts # Params/response types
├── {action}.ts # Individual tool (e.g., send_message.ts)
└── ...
```
**Tool file structure:**
```typescript
// tools/{service}/{action}.ts
import type { {Service}Params, {Service}Response } from '@/tools/{service}/types'
import type { ToolConfig } from '@/tools/types'
export const {service}{Action}Tool: ToolConfig<{Service}Params, {Service}Response> = {
id: '{service}_{action}',
name: '{Service} {Action}',
description: 'What this tool does',
version: '1.0.0',
oauth: { required: true, provider: '{service}' }, // if OAuth
params: { /* param definitions */ },
request: {
url: '/api/tools/{service}/{action}',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({ ...params }),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) throw new Error(data.error)
return { success: true, output: data.output }
},
outputs: { /* output definitions */ },
}
```
**Register in `tools/registry.ts`:**
```typescript
import { {service}{Action}Tool } from '@/tools/{service}'
// Add to registry object
{service}_{action}: {service}{Action}Tool,
```
## 2. Block (`blocks/blocks/{service}.ts`)
```typescript
import { {Service}Icon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { {Service}Response } from '@/tools/{service}/types'
export const {Service}Block: BlockConfig<{Service}Response> = {
type: '{service}',
name: '{Service}',
description: 'Short description',
longDescription: 'Detailed description',
category: 'tools',
bgColor: '#hexcolor',
icon: {Service}Icon,
subBlocks: [ /* see SubBlock Properties below */ ],
tools: {
access: ['{service}_{action}', ...],
config: {
tool: (params) => `{service}_${params.operation}`,
params: (params) => ({ ...params }),
},
},
inputs: { /* input definitions */ },
outputs: { /* output definitions */ },
}
```
### SubBlock Properties
```typescript
{
id: 'fieldName', // Unique identifier
title: 'Field Label', // UI label
type: 'short-input', // See SubBlock Types below
placeholder: 'Hint text',
required: true, // See Required below
condition: { ... }, // See Condition below
dependsOn: ['otherField'], // See DependsOn below
mode: 'basic', // 'basic' | 'advanced' | 'both' | 'trigger'
}
```
**SubBlock Types:** `short-input`, `long-input`, `dropdown`, `code`, `switch`, `slider`, `oauth-input`, `channel-selector`, `user-selector`, `file-upload`, etc.
### `condition` - Show/hide based on another field
```typescript
// Show when operation === 'send'
condition: { field: 'operation', value: 'send' }
// Show when operation is 'send' OR 'read'
condition: { field: 'operation', value: ['send', 'read'] }
// Show when operation !== 'send'
condition: { field: 'operation', value: 'send', not: true }
// Complex: NOT in list AND another condition
condition: {
field: 'operation',
value: ['list_channels', 'list_users'],
not: true,
and: { field: 'destinationType', value: 'dm', not: true }
}
```
### `required` - Field validation
```typescript
// Always required
required: true
// Conditionally required (same syntax as condition)
required: { field: 'operation', value: 'send' }
```
### `dependsOn` - Clear field when dependencies change
```typescript
// Clear when credential changes
dependsOn: ['credential']
// Clear when authMethod changes AND (credential OR botToken) changes
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] }
```
### `mode` - When to show field
- `'basic'` - Only in basic mode (default UI)
- `'advanced'` - Only in advanced mode (manual input)
- `'both'` - Show in both modes (default)
- `'trigger'` - Only when block is used as trigger
### `canonicalParamId` - Link basic/advanced alternatives
Use to map multiple UI inputs to a single logical parameter:
```typescript
// Basic mode: Visual selector
{
id: 'fileSelector',
type: 'file-selector',
mode: 'basic',
canonicalParamId: 'fileId',
required: true,
},
// Advanced mode: Manual input
{
id: 'manualFileId',
type: 'short-input',
mode: 'advanced',
canonicalParamId: 'fileId',
required: true,
},
```
**Critical Rules:**
- `canonicalParamId` must NOT match any subblock's `id`
- `canonicalParamId` must be unique per operation/condition context
- **Required consistency:** All subblocks in a canonical group must have the same `required` status
- **Inputs section:** Must list canonical param IDs (e.g., `fileId`), NOT raw subblock IDs
- **Params function:** Must use canonical param IDs (raw IDs are deleted after canonical transformation)
**Register in `blocks/registry.ts`:**
```typescript
import { {Service}Block } from '@/blocks/blocks/{service}'
// Add to registry object (alphabetically)
{service}: {Service}Block,
```
## 3. Icon (`components/icons.tsx`)
```typescript
export function {Service}Icon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* SVG path from service's brand assets */}
</svg>
)
}
```
## 4. Trigger (`triggers/{service}/`) - Optional
```
triggers/{service}/
├── index.ts # Export all triggers
├── webhook.ts # Webhook handler
├── utils.ts # Shared utilities
└── {event}.ts # Specific event handlers
```
**Register in `triggers/registry.ts`:**
```typescript
import { {service}WebhookTrigger } from '@/triggers/{service}'
// Add to TRIGGER_REGISTRY
{service}_webhook: {service}WebhookTrigger,
```
## File Handling
When integrations handle file uploads/downloads, use `UserFile` objects consistently.
### File Input (Uploads)
1. **Block subBlocks:** Use basic/advanced mode pattern with `canonicalParamId`
2. **Normalize in block config:** Use `normalizeFileInput` from `@/blocks/utils`
3. **Internal API route:** Create route that uses `downloadFileFromStorage` to get file content
4. **Tool routes to internal API:** Don't call external APIs directly with files
```typescript
// In block tools.config:
import { normalizeFileInput } from '@/blocks/utils'
const normalizedFile = normalizeFileInput(
params.uploadFile || params.fileRef || params.fileContent,
{ single: true }
)
if (normalizedFile) params.file = normalizedFile
```
### File Output (Downloads)
Use `FileToolProcessor` in tool `transformResponse` to store files:
```typescript
import { FileToolProcessor } from '@/executor/utils/file-tool-processor'
const processor = new FileToolProcessor(context)
const file = await processor.processFileData({
data: base64Content,
mimeType: 'application/pdf',
filename: 'doc.pdf',
})
```
### Key Helpers
| Helper | Location | Purpose |
|--------|----------|---------|
| `normalizeFileInput` | `@/blocks/utils` | Normalize file params in block config |
| `processFilesToUserFiles` | `@/lib/uploads/utils/file-utils` | Convert raw inputs to UserFile[] |
| `downloadFileFromStorage` | `@/lib/uploads/utils/file-utils.server` | Get Buffer from UserFile |
| `FileToolProcessor` | `@/executor/utils/file-tool-processor` | Process tool output files |
## Checklist
- [ ] Look up API docs for the service
- [ ] Create `tools/{service}/types.ts` with proper types
- [ ] Create tool files for each operation
- [ ] Create `tools/{service}/index.ts` barrel export
- [ ] Register tools in `tools/registry.ts`
- [ ] Add icon to `components/icons.tsx`
- [ ] Create block in `blocks/blocks/{service}.ts`
- [ ] Register block in `blocks/registry.ts`
- [ ] (Optional) Create triggers in `triggers/{service}/`
- [ ] (Optional) Register triggers in `triggers/registry.ts`
- [ ] (If file uploads) Create internal API route with `downloadFileFromStorage`
- [ ] (If file uploads) Use `normalizeFileInput` in block config

View File

@@ -0,0 +1,66 @@
---
paths:
- "apps/sim/hooks/queries/**/*.ts"
---
# React Query Patterns
All React Query hooks live in `hooks/queries/`.
## Query Key Factory
Every query file defines a keys factory:
```typescript
export const entityKeys = {
all: ['entity'] as const,
list: (workspaceId?: string) => [...entityKeys.all, 'list', workspaceId ?? ''] as const,
detail: (id?: string) => [...entityKeys.all, 'detail', id ?? ''] as const,
}
```
## File Structure
```typescript
// 1. Query keys factory
// 2. Types (if needed)
// 3. Private fetch functions
// 4. Exported hooks
```
## Query Hook
```typescript
export function useEntityList(workspaceId?: string, options?: { enabled?: boolean }) {
return useQuery({
queryKey: entityKeys.list(workspaceId),
queryFn: () => fetchEntities(workspaceId as string),
enabled: Boolean(workspaceId) && (options?.enabled ?? true),
staleTime: 60 * 1000,
placeholderData: keepPreviousData,
})
}
```
## Mutation Hook
```typescript
export function useCreateEntity() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (variables) => { /* fetch POST */ },
onSuccess: () => queryClient.invalidateQueries({ queryKey: entityKeys.all }),
})
}
```
## Optimistic Updates
For optimistic mutations syncing with Zustand, use `createOptimisticMutationHandlers` from `@/hooks/queries/utils/optimistic-mutation`.
## Naming
- **Keys**: `entityKeys`
- **Query hooks**: `useEntity`, `useEntityList`
- **Mutation hooks**: `useCreateEntity`, `useUpdateEntity`
- **Fetch functions**: `fetchEntity` (private)

View File

@@ -0,0 +1,71 @@
---
paths:
- "apps/sim/**/store.ts"
- "apps/sim/**/stores/**/*.ts"
---
# Zustand Store Patterns
Stores live in `stores/`. Complex stores split into `store.ts` + `types.ts`.
## Basic Store
```typescript
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
import type { FeatureState } from '@/stores/feature/types'
const initialState = { items: [] as Item[], activeId: null as string | null }
export const useFeatureStore = create<FeatureState>()(
devtools(
(set, get) => ({
...initialState,
setItems: (items) => set({ items }),
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
reset: () => set(initialState),
}),
{ name: 'feature-store' }
)
)
```
## Persisted Store
```typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
export const useFeatureStore = create<FeatureState>()(
persist(
(set) => ({
width: 300,
setWidth: (width) => set({ width }),
_hasHydrated: false,
setHasHydrated: (v) => set({ _hasHydrated: v }),
}),
{
name: 'feature-state',
partialize: (state) => ({ width: state.width }),
onRehydrateStorage: () => (state) => state?.setHasHydrated(true),
}
)
)
```
## Rules
1. Use `devtools` middleware (named stores)
2. Use `persist` only when data should survive reload
3. `partialize` to persist only necessary state
4. `_hasHydrated` pattern for persisted stores needing hydration tracking
5. Immutable updates only
6. `set((state) => ...)` when depending on previous state
7. Provide `reset()` action
## Outside React
```typescript
const items = useFeatureStore.getState().items
useFeatureStore.setState({ items: newItems })
```

View File

@@ -0,0 +1,41 @@
---
paths:
- "apps/sim/**/*.tsx"
- "apps/sim/**/*.css"
---
# Styling Rules
## Tailwind
1. **No inline styles** - Use Tailwind classes
2. **No duplicate dark classes** - Skip `dark:` when value matches light mode
3. **Exact values** - `text-[14px]`, `h-[26px]`
4. **Transitions** - `transition-colors` for interactive states
## Conditional Classes
```typescript
import { cn } from '@/lib/utils'
<div className={cn(
'base-classes',
isActive && 'active-classes',
disabled ? 'opacity-60' : 'hover:bg-accent'
)} />
```
## CSS Variables
For dynamic values (widths, heights) synced with stores:
```typescript
// In store
setWidth: (width) => {
set({ width })
document.documentElement.style.setProperty('--sidebar-width', `${width}px`)
}
// In component
<aside style={{ width: 'var(--sidebar-width)' }} />
```

View File

@@ -0,0 +1,217 @@
---
paths:
- "apps/sim/**/*.test.ts"
- "apps/sim/**/*.test.tsx"
---
# Testing Patterns
Use Vitest. Test files: `feature.ts``feature.test.ts`
## Global Mocks (vitest.setup.ts)
These modules are mocked globally — do NOT re-mock them in test files unless you need to override behavior:
- `@sim/db``databaseMock`
- `drizzle-orm``drizzleOrmMock`
- `@sim/logger``loggerMock`
- `@/stores/console/store`, `@/stores/terminal`, `@/stores/execution/store`
- `@/blocks/registry`
- `@trigger.dev/sdk`
## Structure
```typescript
/**
* @vitest-environment node
*/
import { createMockRequest } from '@sim/testing'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const { mockGetSession } = vi.hoisted(() => ({
mockGetSession: vi.fn(),
}))
vi.mock('@/lib/auth', () => ({
auth: { api: { getSession: vi.fn() } },
getSession: mockGetSession,
}))
import { GET, POST } from '@/app/api/my-route/route'
describe('my route', () => {
beforeEach(() => {
vi.clearAllMocks()
mockGetSession.mockResolvedValue({ user: { id: 'user-1' } })
})
it('returns data', async () => {
const req = createMockRequest('GET')
const res = await GET(req)
expect(res.status).toBe(200)
})
})
```
## Performance Rules (Critical)
### NEVER use `vi.resetModules()` + `vi.doMock()` + `await import()`
This is the #1 cause of slow tests. It forces complete module re-evaluation per test.
```typescript
// BAD — forces module re-evaluation every test (~50-100ms each)
beforeEach(() => {
vi.resetModules()
vi.doMock('@/lib/auth', () => ({ getSession: vi.fn() }))
})
it('test', async () => {
const { GET } = await import('./route') // slow dynamic import
})
// GOOD — module loaded once, mocks reconfigured per test (~1ms each)
const { mockGetSession } = vi.hoisted(() => ({
mockGetSession: vi.fn(),
}))
vi.mock('@/lib/auth', () => ({ getSession: mockGetSession }))
import { GET } from '@/app/api/my-route/route'
beforeEach(() => { vi.clearAllMocks() })
it('test', () => {
mockGetSession.mockResolvedValue({ user: { id: '1' } })
})
```
**Only exception:** Singleton modules that cache state at module scope (e.g., Redis clients, connection pools). These genuinely need `vi.resetModules()` + dynamic import to get a fresh instance per test.
### NEVER use `vi.importActual()`
This defeats the purpose of mocking by loading the real module and all its dependencies.
```typescript
// BAD — loads real module + all transitive deps
vi.mock('@/lib/workspaces/utils', async () => {
const actual = await vi.importActual('@/lib/workspaces/utils')
return { ...actual, myFn: vi.fn() }
})
// GOOD — mock everything, only implement what tests need
vi.mock('@/lib/workspaces/utils', () => ({
myFn: vi.fn(),
otherFn: vi.fn(),
}))
```
### NEVER use `mockAuth()`, `mockConsoleLogger()`, or `setupCommonApiMocks()` from `@sim/testing`
These helpers internally use `vi.doMock()` which is slow. Use direct `vi.hoisted()` + `vi.mock()` instead.
### Mock heavy transitive dependencies
If a module under test imports `@/blocks` (200+ files), `@/tools/registry`, or other heavy modules, mock them:
```typescript
vi.mock('@/blocks', () => ({
getBlock: () => null,
getAllBlocks: () => ({}),
getAllBlockTypes: () => [],
registry: {},
}))
```
### Use `@vitest-environment node` unless DOM is needed
Only use `@vitest-environment jsdom` if the test uses `window`, `document`, `FormData`, or other browser APIs. Node environment is significantly faster.
### Avoid real timers in tests
```typescript
// BAD
await new Promise(r => setTimeout(r, 500))
// GOOD — use minimal delays or fake timers
await new Promise(r => setTimeout(r, 1))
// or
vi.useFakeTimers()
```
## Mock Pattern Reference
### Auth mocking (API routes)
```typescript
const { mockGetSession } = vi.hoisted(() => ({
mockGetSession: vi.fn(),
}))
vi.mock('@/lib/auth', () => ({
auth: { api: { getSession: vi.fn() } },
getSession: mockGetSession,
}))
// In tests:
mockGetSession.mockResolvedValue({ user: { id: 'user-1', email: 'test@example.com' } })
mockGetSession.mockResolvedValue(null) // unauthenticated
```
### Hybrid auth mocking
```typescript
const { mockCheckSessionOrInternalAuth } = vi.hoisted(() => ({
mockCheckSessionOrInternalAuth: vi.fn(),
}))
vi.mock('@/lib/auth/hybrid', () => ({
checkSessionOrInternalAuth: mockCheckSessionOrInternalAuth,
}))
// In tests:
mockCheckSessionOrInternalAuth.mockResolvedValue({
success: true, userId: 'user-1', authType: 'session',
})
```
### Database chain mocking
```typescript
const { mockSelect, mockFrom, mockWhere } = vi.hoisted(() => ({
mockSelect: vi.fn(),
mockFrom: vi.fn(),
mockWhere: vi.fn(),
}))
vi.mock('@sim/db', () => ({
db: { select: mockSelect },
}))
beforeEach(() => {
mockSelect.mockReturnValue({ from: mockFrom })
mockFrom.mockReturnValue({ where: mockWhere })
mockWhere.mockResolvedValue([{ id: '1', name: 'test' }])
})
```
## @sim/testing Package
Always prefer over local test data.
| Category | Utilities |
|----------|-----------|
| **Mocks** | `loggerMock`, `databaseMock`, `drizzleOrmMock`, `setupGlobalFetchMock()` |
| **Factories** | `createSession()`, `createWorkflowRecord()`, `createBlock()`, `createExecutionContext()` |
| **Builders** | `WorkflowBuilder`, `ExecutionContextBuilder` |
| **Assertions** | `expectWorkflowAccessGranted()`, `expectBlockExecuted()` |
| **Requests** | `createMockRequest()`, `createEnvMock()` |
## Rules Summary
1. `@vitest-environment node` unless DOM is required
2. `vi.hoisted()` + `vi.mock()` + static imports — never `vi.resetModules()` + `vi.doMock()` + dynamic imports
3. `vi.mock()` calls before importing mocked modules
4. `@sim/testing` utilities over local mocks
5. `beforeEach(() => vi.clearAllMocks())` to reset state — no redundant `afterEach`
6. No `vi.importActual()` — mock everything explicitly
7. No `mockAuth()`, `mockConsoleLogger()`, `setupCommonApiMocks()` — use direct mocks
8. Mock heavy deps (`@/blocks`, `@/tools/registry`, `@/triggers`) in tests that don't need them
9. Use absolute imports in test files
10. Avoid real timers — use 1ms delays or `vi.useFakeTimers()`

View File

@@ -0,0 +1,21 @@
---
paths:
- "apps/sim/**/*.ts"
- "apps/sim/**/*.tsx"
---
# TypeScript Rules
1. **No `any`** - Use proper types or `unknown` with type guards
2. **Props interface** - Always define for components
3. **Const assertions** - `as const` for constant objects/arrays
4. **Ref types** - Explicit: `useRef<HTMLDivElement>(null)`
5. **Type imports** - `import type { X }` for type-only imports
```typescript
// ✗ Bad
const handleClick = (e: any) => {}
// ✓ Good
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {}
```

View File

@@ -0,0 +1,7 @@
Based on the given area of interest, please:
1. Dig around the codebase in terms of that given area of interest, gather general information such as keywords and architecture overview.
2. Spawn off n=10 (unless specified otherwise) task agents to dig deeper into the codebase in terms of that given area of interest, some of them should be out of the box for variance.
3. Once the task agents are done, use the information to do what the user wants.
If user is in plan mode, use the information to create the plan.

View File

@@ -1,45 +1,35 @@
---
description: EMCN component library patterns with CVA
description: EMCN component library patterns
globs: ["apps/sim/components/emcn/**"]
---
# EMCN Component Guidelines
# EMCN Components
## When to Use CVA vs Direct Styles
Import from `@/components/emcn`, never from subpaths (except CSS files).
**Use CVA (class-variance-authority) when:**
- 2+ visual variants (primary, secondary, outline)
- Multiple sizes or state variations
- Example: Button with variants
## CVA vs Direct Styles
**Use direct className when:**
- Single consistent style
- No variations needed
- Example: Label with one style
**Use CVA when:** 2+ variants (primary/secondary, sm/md/lg)
## Patterns
**With CVA:**
```tsx
const buttonVariants = cva('base-classes', {
variants: {
variant: { default: '...', primary: '...' },
size: { sm: '...', md: '...' }
}
variants: { variant: { default: '...', primary: '...' } }
})
export { Button, buttonVariants }
```
**Without CVA:**
**Use direct className when:** Single consistent style, no variations
```tsx
function Label({ className, ...props }) {
return <Primitive className={cn('single-style-classes', className)} {...props} />
return <Primitive className={cn('style-classes', className)} {...props} />
}
```
## Rules
- Use Radix UI primitives for accessibility
- Export component and variants (if using CVA)
- TSDoc with usage examples
- Consistent tokens: `font-medium`, `text-[12px]`, `rounded-[4px]`
- Always use `transition-colors` for hover states
- `transition-colors` for hover states

View File

@@ -8,7 +8,7 @@ alwaysApply: true
You are a professional software engineer. All code must follow best practices: accurate, readable, clean, and efficient.
## Logging
Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`.
Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`.
## Comments
Use TSDoc for documentation. No `====` separators. No non-TSDoc comments.

View File

@@ -0,0 +1,26 @@
---
description: SEO and GEO guidelines for the landing page
globs: ["apps/sim/app/(home)/**/*.tsx"]
---
# Landing Page — SEO / GEO
## SEO
- One `<h1>` per page, in Hero only — never add another.
- Strict heading hierarchy: H1 (Hero) → H2 (section titles) → H3 (feature names).
- Every section: `<section id="…" aria-labelledby="…-heading">`.
- Decorative/animated elements: `aria-hidden="true"`.
- All internal routes use Next.js `<Link>` (crawlable). External links get `rel="noopener noreferrer"`.
- Navbar is a Server Component (no `'use client'`) for immediate crawlability. Logo `<Image>` has `priority` (LCP element).
- Navbar `<nav>` carries `SiteNavigationElement` schema.org markup.
- Feature lists must stay in sync with `WebApplication.featureList` in `structured-data.tsx`.
## GEO (Generative Engine Optimisation)
- **Answer-first pattern**: each section's H2 + subtitle should directly answer a user question (e.g. "What is Sim?", "How fast can I deploy?").
- **Atomic answer blocks**: each feature / template card should be independently extractable by an AI summariser.
- **Entity consistency**: always write "Sim" by name — never "the platform" or "our tool".
- **Keyword density**: first 150 visible chars of Hero must name "Sim", "AI agents", "agentic workflows".
- **sr-only summaries**: Hero and Templates each have a `<p className="sr-only">` (~50 words) as an atomic product/catalog summary for AI citation.
- **Specific numbers**: prefer concrete figures ("1,000+ integrations", "15+ AI providers") over vague claims.

View File

@@ -10,58 +10,47 @@ globs: ["apps/sim/**"]
2. **Composition Over Complexity**: Break down complex logic into smaller pieces
3. **Type Safety First**: TypeScript interfaces for all props, state, return types
4. **Predictable State**: Zustand for global state, useState for UI-only concerns
5. **Performance by Default**: useMemo, useCallback, refs appropriately
## File Organization
## Root-Level Structure
```
apps/sim/
├── app/ # Next.js app router (pages, API routes)
├── blocks/ # Block definitions and registry
├── components/ # Shared UI (emcn/, ui/)
├── executor/ # Workflow execution engine
├── hooks/ # Shared hooks (queries/, selectors/)
├── lib/ # App-wide utilities
├── providers/ # LLM provider integrations
├── stores/ # Zustand stores
├── tools/ # Tool definitions
└── triggers/ # Trigger definitions
```
## Feature Organization
Features live under `app/workspace/[workspaceId]/`:
```
feature/
├── components/ # Feature components
│ └── sub-feature/ # Sub-feature with own components
├── hooks/ # Custom hooks
── feature.tsx # Main component
├── components/ # Feature components
├── hooks/ # Feature-scoped hooks
├── utils/ # Feature-scoped utilities (2+ consumers)
── feature.tsx # Main component
└── page.tsx # Next.js page entry
```
## Naming Conventions
- **Components**: PascalCase (`WorkflowList`, `TriggerPanel`)
- **Hooks**: camelCase with `use` prefix (`useWorkflowOperations`)
- **Files**: kebab-case matching export (`workflow-list.tsx`)
- **Stores**: kebab-case in stores/ (`sidebar/store.ts`)
- **Components**: PascalCase (`WorkflowList`)
- **Hooks**: `use` prefix (`useWorkflowOperations`)
- **Files**: kebab-case (`workflow-list.tsx`)
- **Stores**: `stores/feature/store.ts`
- **Constants**: SCREAMING_SNAKE_CASE
- **Interfaces**: PascalCase with suffix (`WorkflowListProps`)
## State Management
## Utils Rules
**useState**: UI-only concerns (dropdown open, hover, form inputs)
**Zustand**: Shared state, persistence, global app state
**useRef**: DOM refs, avoiding dependency issues, mutable non-reactive values
## Component Extraction
**Extract to separate file when:**
- Complex (50+ lines)
- Used across 2+ files
- Has own state/logic
**Keep inline when:**
- Simple (< 10 lines)
- Used in only 1 file
- Purely presentational
**Never import utilities from another component file.** Extract shared helpers to `lib/` or `utils/`.
## Utils Files
**Never create a `utils.ts` file for a single consumer.** Inline the logic directly in the consuming component.
**Create `utils.ts` when:**
- 2+ files import the same helper
**Prefer existing sources of truth:**
- Before duplicating logic, check if a centralized helper already exists (e.g., `lib/logs/get-trigger-options.ts`)
- Import from the source of truth rather than creating wrapper functions
**Location hierarchy:**
- `lib/` — App-wide utilities (auth, billing, core)
- `feature/utils.ts` — Feature-scoped utilities (used by 2+ components in the feature)
- Inline — Single-use helpers (define directly in the component)
- **Never create `utils.ts` for single consumer** - inline it
- **Create `utils.ts` when** 2+ files need the same helper
- **Check existing sources** before duplicating (`lib/` has many utilities)
- **Location**: `lib/` (app-wide) → `feature/utils/` (feature-scoped) → inline (single-use)

View File

@@ -6,59 +6,43 @@ globs: ["apps/sim/**/*.tsx"]
# Component Patterns
## Structure Order
```typescript
'use client' // Only if using hooks
// 1. Imports (external → internal → relative)
// 2. Constants at module level
// Imports (external → internal)
// Constants at module level
const CONFIG = { SPACING: 8 } as const
// 3. Props interface with TSDoc
// Props interface
interface ComponentProps {
/** Description */
requiredProp: string
optionalProp?: boolean
}
// 4. Component with TSDoc
export function Component({ requiredProp, optionalProp = false }: ComponentProps) {
// a. Refs
// b. External hooks (useParams, useRouter)
// c. Store hooks
// d. Custom hooks
// e. Local state
// f. useMemo computations
// g. useCallback handlers
// f. useMemo
// g. useCallback
// h. useEffect
// i. Return JSX
}
```
## Rules
1. Add `'use client'` when using React hooks
1. `'use client'` only when using React hooks
2. Always define props interface
3. TSDoc on component: description, @param, @returns
4. Extract constants with `as const`
5. Use Tailwind only, no inline styles
6. Semantic HTML (`aside`, `nav`, `article`)
7. Include ARIA attributes where appropriate
8. Optional chain callbacks: `onAction?.(id)`
3. Extract constants with `as const`
4. Semantic HTML (`aside`, `nav`, `article`)
5. Optional chain callbacks: `onAction?.(id)`
## Factory Pattern with Caching
## Component Extraction
When generating components for a specific signature (e.g., icons):
**Extract when:** 50+ lines, used in 2+ files, or has own state/logic
```typescript
const cache = new Map<string, React.ComponentType<{ className?: string }>>()
function getColorIcon(color: string) {
if (cache.has(color)) return cache.get(color)!
const Icon = ({ className }: { className?: string }) => (
<div className={cn(className, 'rounded-[3px]')} style={{ backgroundColor: color, width: 10, height: 10 }} />
)
Icon.displayName = `ColorIcon(${color})`
cache.set(color, Icon)
return Icon
}
```
**Keep inline when:** < 10 lines, single use, purely presentational

View File

@@ -6,21 +6,13 @@ globs: ["apps/sim/**/use-*.ts", "apps/sim/**/hooks/**/*.ts"]
# Hook Patterns
## Structure
```typescript
import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('useFeatureName')
interface UseFeatureProps {
id: string
onSuccess?: (result: Result) => void
}
/**
* Hook description.
* @param props - Configuration
* @returns State and operations
*/
export function useFeature({ id, onSuccess }: UseFeatureProps) {
// 1. Refs for stable dependencies
const idRef = useRef(id)
@@ -29,7 +21,6 @@ export function useFeature({ id, onSuccess }: UseFeatureProps) {
// 2. State
const [data, setData] = useState<Data | null>(null)
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
// 3. Sync refs
useEffect(() => {
@@ -37,32 +28,27 @@ export function useFeature({ id, onSuccess }: UseFeatureProps) {
onSuccessRef.current = onSuccess
}, [id, onSuccess])
// 4. Operations with useCallback
// 4. Operations (useCallback with empty deps when using refs)
const fetchData = useCallback(async () => {
setIsLoading(true)
try {
const result = await fetch(`/api/${idRef.current}`).then(r => r.json())
setData(result)
onSuccessRef.current?.(result)
} catch (err) {
setError(err as Error)
logger.error('Failed', { error: err })
} finally {
setIsLoading(false)
}
}, []) // Empty deps - using refs
}, [])
// 5. Return grouped by state/operations
return { data, isLoading, error, fetchData }
return { data, isLoading, fetchData }
}
```
## Rules
1. Single responsibility per hook
2. Props interface required
3. TSDoc required
4. Use logger, not console.log
5. Refs for stable callback dependencies
6. Wrap returned functions in useCallback
7. Always try/catch async operations
8. Track loading/error states
3. Refs for stable callback dependencies
4. Wrap returned functions in useCallback
5. Always try/catch async operations
6. Track loading/error states

View File

@@ -5,33 +5,57 @@ globs: ["apps/sim/**/*.ts", "apps/sim/**/*.tsx"]
# Import Patterns
## EMCN Components
Import from `@/components/emcn`, never from subpaths like `@/components/emcn/components/modal/modal`.
## Absolute Imports
**Exception**: CSS imports use actual file paths: `import '@/components/emcn/components/code/code.css'`
**Always use absolute imports.** Never use relative imports.
## Feature Components
Import from central folder indexes, not specific subfolders:
```typescript
// ✅ Correct
import { Dashboard, Sidebar } from '@/app/workspace/[workspaceId]/logs/components'
// ✓ Good
import { useWorkflowStore } from '@/stores/workflows/store'
import { Button } from '@/components/ui/button'
// ❌ Wrong
import { Dashboard } from '@/app/workspace/[workspaceId]/logs/components/dashboard'
// ✗ Bad
import { useWorkflowStore } from '../../../stores/workflows/store'
```
## Internal vs External
- **Cross-feature**: Absolute paths through central index
- **Within feature**: Relative paths (`./components/...`, `../utils`)
## Barrel Exports
Use barrel exports (`index.ts`) when a folder has 3+ exports. Import from barrel, not individual files.
```typescript
// ✓ Good
import { Dashboard, Sidebar } from '@/app/workspace/[workspaceId]/logs/components'
// ✗ Bad
import { Dashboard } from '@/app/workspace/[workspaceId]/logs/components/dashboard/dashboard'
```
## No Re-exports
Do not re-export from non-barrel files. Import directly from the source.
```typescript
// ✓ Good - import from where it's declared
import { CORE_TRIGGER_TYPES } from '@/stores/logs/filters/types'
// ✗ Bad - re-exporting in utils.ts then importing from there
import { CORE_TRIGGER_TYPES } from '@/app/workspace/.../utils'
```
## Import Order
1. React/core libraries
2. External libraries
3. UI components (`@/components/emcn`, `@/components/ui`)
4. Utilities (`@/lib/...`)
5. Feature imports from indexes
6. Relative imports
5. Stores (`@/stores/...`)
6. Feature imports
7. CSS imports
## Types
Use `type` keyword: `import type { WorkflowLog } from '...'`
## Type Imports
Use `type` keyword for type-only imports:
```typescript
import type { WorkflowLog } from '@/stores/logs/types'
```

View File

@@ -0,0 +1,285 @@
---
description: Adding new integrations (tools, blocks, triggers)
globs: ["apps/sim/tools/**", "apps/sim/blocks/**", "apps/sim/triggers/**"]
---
# Adding Integrations
## Overview
Adding a new integration typically requires:
1. **Tools** - API operations (`tools/{service}/`)
2. **Block** - UI component (`blocks/blocks/{service}.ts`)
3. **Icon** - SVG icon (`components/icons.tsx`)
4. **Trigger** (optional) - Webhooks/polling (`triggers/{service}/`)
Always look up the service's API docs first.
## 1. Tools (`tools/{service}/`)
```
tools/{service}/
├── index.ts # Export all tools
├── types.ts # Params/response types
├── {action}.ts # Individual tool (e.g., send_message.ts)
└── ...
```
**Tool file structure:**
```typescript
// tools/{service}/{action}.ts
import type { {Service}Params, {Service}Response } from '@/tools/{service}/types'
import type { ToolConfig } from '@/tools/types'
export const {service}{Action}Tool: ToolConfig<{Service}Params, {Service}Response> = {
id: '{service}_{action}',
name: '{Service} {Action}',
description: 'What this tool does',
version: '1.0.0',
oauth: { required: true, provider: '{service}' }, // if OAuth
params: { /* param definitions */ },
request: {
url: '/api/tools/{service}/{action}',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({ ...params }),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) throw new Error(data.error)
return { success: true, output: data.output }
},
outputs: { /* output definitions */ },
}
```
**Register in `tools/registry.ts`:**
```typescript
import { {service}{Action}Tool } from '@/tools/{service}'
// Add to registry object
{service}_{action}: {service}{Action}Tool,
```
## 2. Block (`blocks/blocks/{service}.ts`)
```typescript
import { {Service}Icon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { {Service}Response } from '@/tools/{service}/types'
export const {Service}Block: BlockConfig<{Service}Response> = {
type: '{service}',
name: '{Service}',
description: 'Short description',
longDescription: 'Detailed description',
category: 'tools',
bgColor: '#hexcolor',
icon: {Service}Icon,
subBlocks: [ /* see SubBlock Properties below */ ],
tools: {
access: ['{service}_{action}', ...],
config: {
tool: (params) => `{service}_${params.operation}`,
params: (params) => ({ ...params }),
},
},
inputs: { /* input definitions */ },
outputs: { /* output definitions */ },
}
```
### SubBlock Properties
```typescript
{
id: 'fieldName', // Unique identifier
title: 'Field Label', // UI label
type: 'short-input', // See SubBlock Types below
placeholder: 'Hint text',
required: true, // See Required below
condition: { ... }, // See Condition below
dependsOn: ['otherField'], // See DependsOn below
mode: 'basic', // 'basic' | 'advanced' | 'both' | 'trigger'
}
```
**SubBlock Types:** `short-input`, `long-input`, `dropdown`, `code`, `switch`, `slider`, `oauth-input`, `channel-selector`, `user-selector`, `file-upload`, etc.
### `condition` - Show/hide based on another field
```typescript
// Show when operation === 'send'
condition: { field: 'operation', value: 'send' }
// Show when operation is 'send' OR 'read'
condition: { field: 'operation', value: ['send', 'read'] }
// Show when operation !== 'send'
condition: { field: 'operation', value: 'send', not: true }
// Complex: NOT in list AND another condition
condition: {
field: 'operation',
value: ['list_channels', 'list_users'],
not: true,
and: { field: 'destinationType', value: 'dm', not: true }
}
```
### `required` - Field validation
```typescript
// Always required
required: true
// Conditionally required (same syntax as condition)
required: { field: 'operation', value: 'send' }
```
### `dependsOn` - Clear field when dependencies change
```typescript
// Clear when credential changes
dependsOn: ['credential']
// Clear when authMethod changes AND (credential OR botToken) changes
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] }
```
### `mode` - When to show field
- `'basic'` - Only in basic mode (default UI)
- `'advanced'` - Only in advanced mode (manual input)
- `'both'` - Show in both modes (default)
- `'trigger'` - Only when block is used as trigger
### `canonicalParamId` - Link basic/advanced alternatives
Use to map multiple UI inputs to a single logical parameter:
```typescript
// Basic mode: Visual selector
{
id: 'fileSelector',
type: 'file-selector',
mode: 'basic',
canonicalParamId: 'fileId',
required: true,
},
// Advanced mode: Manual input
{
id: 'manualFileId',
type: 'short-input',
mode: 'advanced',
canonicalParamId: 'fileId',
required: true,
},
```
**Critical Rules:**
- `canonicalParamId` must NOT match any subblock's `id`
- `canonicalParamId` must be unique per operation/condition context
- **Required consistency:** All subblocks in a canonical group must have the same `required` status
- **Inputs section:** Must list canonical param IDs (e.g., `fileId`), NOT raw subblock IDs
- **Params function:** Must use canonical param IDs (raw IDs are deleted after canonical transformation)
**Register in `blocks/registry.ts`:**
```typescript
import { {Service}Block } from '@/blocks/blocks/{service}'
// Add to registry object (alphabetically)
{service}: {Service}Block,
```
## 3. Icon (`components/icons.tsx`)
```typescript
export function {Service}Icon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* SVG path from service's brand assets */}
</svg>
)
}
```
## 4. Trigger (`triggers/{service}/`) - Optional
```
triggers/{service}/
├── index.ts # Export all triggers
├── webhook.ts # Webhook handler
├── utils.ts # Shared utilities
└── {event}.ts # Specific event handlers
```
**Register in `triggers/registry.ts`:**
```typescript
import { {service}WebhookTrigger } from '@/triggers/{service}'
// Add to TRIGGER_REGISTRY
{service}_webhook: {service}WebhookTrigger,
```
## File Handling
When integrations handle file uploads/downloads, use `UserFile` objects consistently.
### File Input (Uploads)
1. **Block subBlocks:** Use basic/advanced mode pattern with `canonicalParamId`
2. **Normalize in block config:** Use `normalizeFileInput` from `@/blocks/utils`
3. **Internal API route:** Create route that uses `downloadFileFromStorage` to get file content
4. **Tool routes to internal API:** Don't call external APIs directly with files
```typescript
// In block tools.config:
import { normalizeFileInput } from '@/blocks/utils'
const normalizedFile = normalizeFileInput(
params.uploadFile || params.fileRef || params.fileContent,
{ single: true }
)
if (normalizedFile) params.file = normalizedFile
```
### File Output (Downloads)
Use `FileToolProcessor` in tool `transformResponse` to store files:
```typescript
import { FileToolProcessor } from '@/executor/utils/file-tool-processor'
const processor = new FileToolProcessor(context)
const file = await processor.processFileData({
data: base64Content,
mimeType: 'application/pdf',
filename: 'doc.pdf',
})
```
### Key Helpers
| Helper | Location | Purpose |
|--------|----------|---------|
| `normalizeFileInput` | `@/blocks/utils` | Normalize file params in block config |
| `processFilesToUserFiles` | `@/lib/uploads/utils/file-utils` | Convert raw inputs to UserFile[] |
| `downloadFileFromStorage` | `@/lib/uploads/utils/file-utils.server` | Get Buffer from UserFile |
| `FileToolProcessor` | `@/executor/utils/file-tool-processor` | Process tool output files |
## Checklist
- [ ] Look up API docs for the service
- [ ] Create `tools/{service}/types.ts` with proper types
- [ ] Create tool files for each operation
- [ ] Create `tools/{service}/index.ts` barrel export
- [ ] Register tools in `tools/registry.ts`
- [ ] Add icon to `components/icons.tsx`
- [ ] Create block in `blocks/blocks/{service}.ts`
- [ ] Register block in `blocks/registry.ts`
- [ ] (Optional) Create triggers in `triggers/{service}/`
- [ ] (Optional) Register triggers in `triggers/registry.ts`
- [ ] (If file uploads) Create internal API route with `downloadFileFromStorage`
- [ ] (If file uploads) Use `normalizeFileInput` in block config

View File

@@ -0,0 +1,126 @@
---
description: React Query patterns for the Sim application
globs: ["apps/sim/hooks/queries/**/*.ts"]
---
# React Query Patterns
All React Query hooks live in `hooks/queries/`. All server state must go through React Query — never use `useState` + `fetch` in components for data fetching or mutations.
## Query Key Factory
Every query file defines a hierarchical keys factory with an `all` root key and intermediate plural keys for prefix-level invalidation:
```typescript
export const entityKeys = {
all: ['entity'] as const,
lists: () => [...entityKeys.all, 'list'] as const,
list: (workspaceId?: string) => [...entityKeys.lists(), workspaceId ?? ''] as const,
details: () => [...entityKeys.all, 'detail'] as const,
detail: (id?: string) => [...entityKeys.details(), id ?? ''] as const,
}
```
Never use inline query keys — always use the factory.
## File Structure
```typescript
// 1. Query keys factory
// 2. Types (if needed)
// 3. Private fetch functions (accept signal parameter)
// 4. Exported hooks
```
## Query Hook
- Every `queryFn` must destructure and forward `signal` for request cancellation
- Every query must have an explicit `staleTime`
- Use `keepPreviousData` only on variable-key queries (where params change), never on static keys
```typescript
async function fetchEntities(workspaceId: string, signal?: AbortSignal) {
const response = await fetch(`/api/entities?workspaceId=${workspaceId}`, { signal })
if (!response.ok) throw new Error('Failed to fetch entities')
return response.json()
}
export function useEntityList(workspaceId?: string, options?: { enabled?: boolean }) {
return useQuery({
queryKey: entityKeys.list(workspaceId),
queryFn: ({ signal }) => fetchEntities(workspaceId as string, signal),
enabled: Boolean(workspaceId) && (options?.enabled ?? true),
staleTime: 60 * 1000,
placeholderData: keepPreviousData, // OK: workspaceId varies
})
}
```
## Mutation Hook
- Use targeted invalidation (`entityKeys.lists()`) not broad (`entityKeys.all`) when possible
- Invalidation must cover all affected query key prefixes (lists, details, related views)
```typescript
export function useCreateEntity() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (variables) => { /* fetch POST */ },
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: entityKeys.lists() })
},
})
}
```
## Optimistic Updates
For optimistic mutations, use `onSettled` (not `onSuccess`) for cache reconciliation — `onSettled` fires on both success and error, ensuring the cache is always reconciled with the server.
```typescript
export function useUpdateEntity() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (variables) => { /* ... */ },
onMutate: async (variables) => {
await queryClient.cancelQueries({ queryKey: entityKeys.detail(variables.id) })
const previous = queryClient.getQueryData(entityKeys.detail(variables.id))
queryClient.setQueryData(entityKeys.detail(variables.id), /* optimistic value */)
return { previous }
},
onError: (_err, variables, context) => {
queryClient.setQueryData(entityKeys.detail(variables.id), context?.previous)
},
onSettled: (_data, _error, variables) => {
queryClient.invalidateQueries({ queryKey: entityKeys.lists() })
queryClient.invalidateQueries({ queryKey: entityKeys.detail(variables.id) })
},
})
}
```
For optimistic mutations syncing with Zustand, use `createOptimisticMutationHandlers` from `@/hooks/queries/utils/optimistic-mutation`.
## useCallback Dependencies
Never include mutation objects (e.g., `createEntity`) in `useCallback` dependency arrays — the mutation object is not referentially stable and changes on every state update. The `.mutate()` and `.mutateAsync()` functions are stable in TanStack Query v5.
```typescript
// ✗ Bad — causes unnecessary recreations
const handler = useCallback(() => {
createEntity.mutate(data)
}, [createEntity]) // unstable reference
// ✓ Good — omit from deps, mutate is stable
const handler = useCallback(() => {
createEntity.mutate(data)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data])
```
## Naming
- **Keys**: `entityKeys`
- **Query hooks**: `useEntity`, `useEntityList`
- **Mutation hooks**: `useCreateEntity`, `useUpdateEntity`, `useDeleteEntity`
- **Fetch functions**: `fetchEntity`, `fetchEntities` (private)

View File

@@ -5,53 +5,66 @@ globs: ["apps/sim/**/store.ts", "apps/sim/**/stores/**/*.ts"]
# Zustand Store Patterns
## Structure
Stores live in `stores/`. Complex stores split into `store.ts` + `types.ts`.
## Basic Store
```typescript
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
import type { FeatureState } from '@/stores/feature/types'
const initialState = { items: [] as Item[], activeId: null as string | null }
export const useFeatureStore = create<FeatureState>()(
devtools(
(set, get) => ({
...initialState,
setItems: (items) => set({ items }),
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
reset: () => set(initialState),
}),
{ name: 'feature-store' }
)
)
```
## Persisted Store
```typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface FeatureState {
// State
items: Item[]
activeId: string | null
// Actions
setItems: (items: Item[]) => void
addItem: (item: Item) => void
clearState: () => void
}
const createInitialState = () => ({
items: [],
activeId: null,
})
export const useFeatureStore = create<FeatureState>()(
persist(
(set) => ({
...createInitialState(),
setItems: (items) => set({ items }),
addItem: (item) => set((state) => ({
items: [...state.items, item],
})),
clearState: () => set(createInitialState()),
width: 300,
setWidth: (width) => set({ width }),
_hasHydrated: false,
setHasHydrated: (v) => set({ _hasHydrated: v }),
}),
{
name: 'feature-state',
partialize: (state) => ({ items: state.items }),
partialize: (state) => ({ width: state.width }),
onRehydrateStorage: () => (state) => state?.setHasHydrated(true),
}
)
)
```
## Rules
1. Interface includes state and actions
2. Extract config to module constants
3. TSDoc on store
4. Only persist what's needed
5. Immutable updates only - never mutate
6. Use `set((state) => ...)` when depending on previous state
7. Provide clear/reset actions
1. Use `devtools` middleware (named stores)
2. Use `persist` only when data should survive reload
3. `partialize` to persist only necessary state
4. `_hasHydrated` pattern for persisted stores needing hydration tracking
5. Immutable updates only
6. `set((state) => ...)` when depending on previous state
7. Provide `reset()` action
## Outside React
```typescript
const items = useFeatureStore.getState().items
useFeatureStore.setState({ items: newItems })
```

View File

@@ -6,13 +6,14 @@ globs: ["apps/sim/**/*.tsx", "apps/sim/**/*.css"]
# Styling Rules
## Tailwind
1. **No inline styles** - Use Tailwind classes exclusively
2. **No duplicate dark classes** - Don't add `dark:` when value matches light mode
3. **Exact values** - Use design system values (`text-[14px]`, `h-[25px]`)
4. **Prefer px** - Use `px-[4px]` over `px-1`
5. **Transitions** - Add `transition-colors` for interactive states
1. **No inline styles** - Use Tailwind classes
2. **No duplicate dark classes** - Skip `dark:` when value matches light mode
3. **Exact values** - `text-[14px]`, `h-[26px]`
4. **Transitions** - `transition-colors` for interactive states
## Conditional Classes
```typescript
import { cn } from '@/lib/utils'
@@ -23,25 +24,17 @@ import { cn } from '@/lib/utils'
)} />
```
## CSS Variables for Dynamic Styles
## CSS Variables
For dynamic values (widths, heights) synced with stores:
```typescript
// In store setter
setSidebarWidth: (width) => {
set({ sidebarWidth: width })
// In store
setWidth: (width) => {
set({ width })
document.documentElement.style.setProperty('--sidebar-width', `${width}px`)
}
// In component
<aside style={{ width: 'var(--sidebar-width)' }} />
```
## Anti-Patterns
```typescript
// ❌ Bad
<div style={{ width: 200 }}>
<div className='text-[var(--text-primary)] dark:text-[var(--text-primary)]'>
// ✅ Good
<div className='w-[200px]'>
<div className='text-[var(--text-primary)]'>
```

View File

@@ -0,0 +1,216 @@
---
description: Testing patterns with Vitest and @sim/testing
globs: ["apps/sim/**/*.test.ts", "apps/sim/**/*.test.tsx"]
---
# Testing Patterns
Use Vitest. Test files: `feature.ts` → `feature.test.ts`
## Global Mocks (vitest.setup.ts)
These modules are mocked globally — do NOT re-mock them in test files unless you need to override behavior:
- `@sim/db` → `databaseMock`
- `drizzle-orm` → `drizzleOrmMock`
- `@sim/logger` → `loggerMock`
- `@/stores/console/store`, `@/stores/terminal`, `@/stores/execution/store`
- `@/blocks/registry`
- `@trigger.dev/sdk`
## Structure
```typescript
/**
* @vitest-environment node
*/
import { createMockRequest } from '@sim/testing'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const { mockGetSession } = vi.hoisted(() => ({
mockGetSession: vi.fn(),
}))
vi.mock('@/lib/auth', () => ({
auth: { api: { getSession: vi.fn() } },
getSession: mockGetSession,
}))
import { GET, POST } from '@/app/api/my-route/route'
describe('my route', () => {
beforeEach(() => {
vi.clearAllMocks()
mockGetSession.mockResolvedValue({ user: { id: 'user-1' } })
})
it('returns data', async () => {
const req = createMockRequest('GET')
const res = await GET(req)
expect(res.status).toBe(200)
})
})
```
## Performance Rules (Critical)
### NEVER use `vi.resetModules()` + `vi.doMock()` + `await import()`
This is the #1 cause of slow tests. It forces complete module re-evaluation per test.
```typescript
// BAD — forces module re-evaluation every test (~50-100ms each)
beforeEach(() => {
vi.resetModules()
vi.doMock('@/lib/auth', () => ({ getSession: vi.fn() }))
})
it('test', async () => {
const { GET } = await import('./route') // slow dynamic import
})
// GOOD — module loaded once, mocks reconfigured per test (~1ms each)
const { mockGetSession } = vi.hoisted(() => ({
mockGetSession: vi.fn(),
}))
vi.mock('@/lib/auth', () => ({ getSession: mockGetSession }))
import { GET } from '@/app/api/my-route/route'
beforeEach(() => { vi.clearAllMocks() })
it('test', () => {
mockGetSession.mockResolvedValue({ user: { id: '1' } })
})
```
**Only exception:** Singleton modules that cache state at module scope (e.g., Redis clients, connection pools). These genuinely need `vi.resetModules()` + dynamic import to get a fresh instance per test.
### NEVER use `vi.importActual()`
This defeats the purpose of mocking by loading the real module and all its dependencies.
```typescript
// BAD — loads real module + all transitive deps
vi.mock('@/lib/workspaces/utils', async () => {
const actual = await vi.importActual('@/lib/workspaces/utils')
return { ...actual, myFn: vi.fn() }
})
// GOOD — mock everything, only implement what tests need
vi.mock('@/lib/workspaces/utils', () => ({
myFn: vi.fn(),
otherFn: vi.fn(),
}))
```
### NEVER use `mockAuth()`, `mockConsoleLogger()`, or `setupCommonApiMocks()` from `@sim/testing`
These helpers internally use `vi.doMock()` which is slow. Use direct `vi.hoisted()` + `vi.mock()` instead.
### Mock heavy transitive dependencies
If a module under test imports `@/blocks` (200+ files), `@/tools/registry`, or other heavy modules, mock them:
```typescript
vi.mock('@/blocks', () => ({
getBlock: () => null,
getAllBlocks: () => ({}),
getAllBlockTypes: () => [],
registry: {},
}))
```
### Use `@vitest-environment node` unless DOM is needed
Only use `@vitest-environment jsdom` if the test uses `window`, `document`, `FormData`, or other browser APIs. Node environment is significantly faster.
### Avoid real timers in tests
```typescript
// BAD
await new Promise(r => setTimeout(r, 500))
// GOOD — use minimal delays or fake timers
await new Promise(r => setTimeout(r, 1))
// or
vi.useFakeTimers()
```
## Mock Pattern Reference
### Auth mocking (API routes)
```typescript
const { mockGetSession } = vi.hoisted(() => ({
mockGetSession: vi.fn(),
}))
vi.mock('@/lib/auth', () => ({
auth: { api: { getSession: vi.fn() } },
getSession: mockGetSession,
}))
// In tests:
mockGetSession.mockResolvedValue({ user: { id: 'user-1', email: 'test@example.com' } })
mockGetSession.mockResolvedValue(null) // unauthenticated
```
### Hybrid auth mocking
```typescript
const { mockCheckSessionOrInternalAuth } = vi.hoisted(() => ({
mockCheckSessionOrInternalAuth: vi.fn(),
}))
vi.mock('@/lib/auth/hybrid', () => ({
checkSessionOrInternalAuth: mockCheckSessionOrInternalAuth,
}))
// In tests:
mockCheckSessionOrInternalAuth.mockResolvedValue({
success: true, userId: 'user-1', authType: 'session',
})
```
### Database chain mocking
```typescript
const { mockSelect, mockFrom, mockWhere } = vi.hoisted(() => ({
mockSelect: vi.fn(),
mockFrom: vi.fn(),
mockWhere: vi.fn(),
}))
vi.mock('@sim/db', () => ({
db: { select: mockSelect },
}))
beforeEach(() => {
mockSelect.mockReturnValue({ from: mockFrom })
mockFrom.mockReturnValue({ where: mockWhere })
mockWhere.mockResolvedValue([{ id: '1', name: 'test' }])
})
```
## @sim/testing Package
Always prefer over local test data.
| Category | Utilities |
|----------|-----------|
| **Mocks** | `loggerMock`, `databaseMock`, `drizzleOrmMock`, `setupGlobalFetchMock()` |
| **Factories** | `createSession()`, `createWorkflowRecord()`, `createBlock()`, `createExecutionContext()` |
| **Builders** | `WorkflowBuilder`, `ExecutionContextBuilder` |
| **Assertions** | `expectWorkflowAccessGranted()`, `expectBlockExecuted()` |
| **Requests** | `createMockRequest()`, `createEnvMock()` |
## Rules Summary
1. `@vitest-environment node` unless DOM is required
2. `vi.hoisted()` + `vi.mock()` + static imports — never `vi.resetModules()` + `vi.doMock()` + dynamic imports
3. `vi.mock()` calls before importing mocked modules
4. `@sim/testing` utilities over local mocks
5. `beforeEach(() => vi.clearAllMocks())` to reset state — no redundant `afterEach`
6. No `vi.importActual()` — mock everything explicitly
7. No `mockAuth()`, `mockConsoleLogger()`, `setupCommonApiMocks()` — use direct mocks
8. Mock heavy deps (`@/blocks`, `@/tools/registry`, `@/triggers`) in tests that don't need them
9. Use absolute imports in test files
10. Avoid real timers — use 1ms delays or `vi.useFakeTimers()`

View File

@@ -6,19 +6,15 @@ globs: ["apps/sim/**/*.ts", "apps/sim/**/*.tsx"]
# TypeScript Rules
1. **No `any`** - Use proper types or `unknown` with type guards
2. **Props interface** - Always define, even for simple components
3. **Callback types** - Full signature with params and return type
4. **Generics** - Use for reusable components/hooks
5. **Const assertions** - `as const` for constant objects/arrays
6. **Ref types** - Explicit: `useRef<HTMLDivElement>(null)`
2. **Props interface** - Always define for components
3. **Const assertions** - `as const` for constant objects/arrays
4. **Ref types** - Explicit: `useRef<HTMLDivElement>(null)`
5. **Type imports** - `import type { X }` for type-only imports
## Anti-Patterns
```typescript
// Bad
// Bad
const handleClick = (e: any) => {}
useEffect(() => { doSomething(prop) }, []) // Missing dep
// Good
// Good
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {}
useEffect(() => { doSomething(prop) }, [prop])
```

View File

@@ -0,0 +1,296 @@
---
name: add-hosted-key
description: Add hosted API key support to a tool so Sim provides the key when users don't bring their own. Use when adding hosted keys, BYOK support, hideWhenHosted, or hosted key pricing to a tool or block.
---
# Adding Hosted Key Support to a Tool
When a tool has hosted key support, Sim provides its own API key if the user hasn't configured one (via BYOK or env var). Usage is metered and billed to the workspace.
## Overview
| Step | What | Where |
|------|------|-------|
| 1 | Register BYOK provider ID | `tools/types.ts`, `app/api/workspaces/[id]/byok-keys/route.ts` |
| 2 | Research the API's pricing and rate limits | API docs / pricing page (before writing any code) |
| 3 | Add `hosting` config to the tool | `tools/{service}/{action}.ts` |
| 4 | Hide API key field when hosted | `blocks/blocks/{service}.ts` |
| 5 | Add to BYOK settings UI | BYOK settings component (`byok.tsx`) |
| 6 | Summarize pricing and throttling comparison | Output to user (after all code changes) |
## Step 1: Register the BYOK Provider ID
Add the new provider to the `BYOKProviderId` union in `tools/types.ts`:
```typescript
export type BYOKProviderId =
| 'openai'
| 'anthropic'
// ...existing providers
| 'your_service'
```
Then add it to `VALID_PROVIDERS` in `app/api/workspaces/[id]/byok-keys/route.ts`:
```typescript
const VALID_PROVIDERS = ['openai', 'anthropic', 'google', 'mistral', 'your_service'] as const
```
## Step 2: Research the API's Pricing Model and Rate Limits
**Before writing any `getCost` or `rateLimit` code**, look up the service's official documentation for both pricing and rate limits. You need to understand:
### Pricing
1. **How the API charges** — per request, per credit, per token, per step, per minute, etc.
2. **Whether the API reports cost in its response** — look for fields like `creditsUsed`, `costDollars`, `tokensUsed`, or similar in the response body or headers
3. **Whether cost varies by endpoint/options** — some APIs charge more for certain features (e.g., Firecrawl charges 1 credit/page base but +4 for JSON format, +4 for enhanced mode)
4. **The dollar-per-unit rate** — what each credit/token/unit costs in dollars on our plan
### Rate Limits
1. **What rate limits the API enforces** — requests per minute/second, tokens per minute, concurrent requests, etc.
2. **Whether limits vary by plan tier** — free vs paid vs enterprise often have different ceilings
3. **Whether limits are per-key or per-account** — determines whether adding more hosted keys actually increases total throughput
4. **What the API returns when rate limited** — HTTP 429, `Retry-After` header, error body format, etc.
5. **Whether there are multiple dimensions** — some APIs limit both requests/min AND tokens/min independently
Search the API's docs/pricing page (use WebSearch/WebFetch). Capture the pricing model as a comment in `getCost` so future maintainers know the source of truth.
### Setting Our Rate Limits
Our rate limiter (`lib/core/rate-limiter/hosted-key/`) uses a token-bucket algorithm applied **per billing actor** (workspace). It supports two modes:
- **`per_request`** — simple; just `requestsPerMinute`. Good when the API charges flat per-request or cost doesn't vary much.
- **`custom`** — `requestsPerMinute` plus additional `dimensions` (e.g., `tokens`, `search_units`). Each dimension has its own `limitPerMinute` and an `extractUsage` function that reads actual usage from the response. Use when the API charges on a variable metric (tokens, credits) and you want to cap that metric too.
When choosing values for `requestsPerMinute` and any dimension limits:
- **Stay well below the API's per-key limit** — our keys are shared across all workspaces. If the API allows 60 RPM per key and we have 3 keys, the global ceiling is ~180 RPM. Set the per-workspace limit low enough (e.g., 20-60 RPM) that many workspaces can coexist without collectively hitting the API's ceiling.
- **Account for key pooling** — our round-robin distributes requests across `N` hosted keys, so the effective API-side rate per key is `(total requests) / N`. But per-workspace limits are enforced *before* key selection, so they apply regardless of key count.
- **Prefer conservative defaults** — it's easy to raise limits later but hard to claw back after users depend on high throughput.
## Step 3: Add `hosting` Config to the Tool
Add a `hosting` object to the tool's `ToolConfig`. This tells the execution layer how to acquire hosted keys, calculate cost, and rate-limit.
```typescript
hosting: {
envKeyPrefix: 'YOUR_SERVICE_API_KEY',
apiKeyParam: 'apiKey',
byokProviderId: 'your_service',
pricing: {
type: 'custom',
getCost: (_params, output) => {
if (output.creditsUsed == null) {
throw new Error('Response missing creditsUsed field')
}
const creditsUsed = output.creditsUsed as number
const cost = creditsUsed * 0.001 // dollars per credit
return { cost, metadata: { creditsUsed } }
},
},
rateLimit: {
mode: 'per_request',
requestsPerMinute: 100,
},
},
```
### Hosted Key Env Var Convention
Keys use a numbered naming pattern driven by a count env var:
```
YOUR_SERVICE_API_KEY_COUNT=3
YOUR_SERVICE_API_KEY_1=sk-...
YOUR_SERVICE_API_KEY_2=sk-...
YOUR_SERVICE_API_KEY_3=sk-...
```
The `envKeyPrefix` value (`YOUR_SERVICE_API_KEY`) determines which env vars are read at runtime. Adding more keys only requires bumping the count and adding the new env var.
### Pricing: Prefer API-Reported Cost
Always prefer using cost data returned by the API (e.g., `creditsUsed`, `costDollars`). This is the most accurate because it accounts for variable pricing tiers, feature modifiers, and plan-level discounts.
**When the API reports cost** — use it directly and throw if missing:
```typescript
pricing: {
type: 'custom',
getCost: (params, output) => {
if (output.creditsUsed == null) {
throw new Error('Response missing creditsUsed field')
}
// $0.001 per credit — from https://example.com/pricing
const cost = (output.creditsUsed as number) * 0.001
return { cost, metadata: { creditsUsed: output.creditsUsed } }
},
},
```
**When the API does NOT report cost** — compute it from params/output based on the pricing docs, but still validate the data you depend on:
```typescript
pricing: {
type: 'custom',
getCost: (params, output) => {
if (!Array.isArray(output.searchResults)) {
throw new Error('Response missing searchResults, cannot determine cost')
}
// Serper: 1 credit for <=10 results, 2 credits for >10 — from https://serper.dev/pricing
const credits = Number(params.num) > 10 ? 2 : 1
return { cost: credits * 0.001, metadata: { credits } }
},
},
```
**`getCost` must always throw** if it cannot determine cost. Never silently fall back to a default — this would hide billing inaccuracies.
### Capturing Cost Data from the API
If the API returns cost info, capture it in `transformResponse` so `getCost` can read it from the output:
```typescript
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
results: data.results,
creditsUsed: data.creditsUsed, // pass through for getCost
},
}
},
```
For async/polling tools, capture it in `postProcess` when the job completes:
```typescript
if (jobData.status === 'completed') {
result.output = {
data: jobData.data,
creditsUsed: jobData.creditsUsed,
}
}
```
## Step 4: Hide the API Key Field When Hosted
In the block config (`blocks/blocks/{service}.ts`), add `hideWhenHosted: true` to the API key subblock. This hides the field on hosted Sim since the platform provides the key:
```typescript
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Enter your API key',
password: true,
required: true,
hideWhenHosted: true,
},
```
The visibility is controlled by `isSubBlockHiddenByHostedKey()` in `lib/workflows/subblocks/visibility.ts`, which checks the `isHosted` feature flag.
### Excluding Specific Operations from Hosted Key Support
When a block has multiple operations but some operations should **not** use a hosted key (e.g., the underlying API is deprecated, unsupported, or too expensive), use the **duplicate apiKey subblock** pattern. This is the same pattern Exa uses for its `research` operation:
1. **Remove the `hosting` config** from the tool definition for that operation — it must not have a `hosting` object at all.
2. **Duplicate the `apiKey` subblock** in the block config with opposing conditions:
```typescript
// API Key — hidden when hosted for operations with hosted key support
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Enter your API key',
password: true,
required: true,
hideWhenHosted: true,
condition: { field: 'operation', value: 'unsupported_op', not: true },
},
// API Key — always visible for unsupported_op (no hosted key support)
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Enter your API key',
password: true,
required: true,
condition: { field: 'operation', value: 'unsupported_op' },
},
```
Both subblocks share the same `id: 'apiKey'`, so the same value flows to the tool. The conditions ensure only one is visible at a time. The first has `hideWhenHosted: true` and shows for all hosted operations; the second has no `hideWhenHosted` and shows only for the excluded operation — meaning users must always provide their own key for that operation.
To exclude multiple operations, use an array: `{ field: 'operation', value: ['op_a', 'op_b'] }`.
**Reference implementations:**
- **Exa** (`blocks/blocks/exa.ts`): `research` operation excluded from hosting — lines 309-329
- **Google Maps** (`blocks/blocks/google_maps.ts`): `speed_limits` operation excluded from hosting (deprecated Roads API)
## Step 5: Add to the BYOK Settings UI
Add an entry to the `PROVIDERS` array in the BYOK settings component so users can bring their own key. You need the service icon from `components/icons.tsx`:
```typescript
{
id: 'your_service',
name: 'Your Service',
icon: YourServiceIcon,
description: 'What this service does',
placeholder: 'Enter your API key',
},
```
## Step 6: Summarize Pricing and Throttling Comparison
After all code changes are complete, output a detailed summary to the user covering:
### What to include
1. **API's pricing model** — how the service charges (per token, per credit, per request, etc.), the specific rates found in docs, and whether the API reports cost in responses.
2. **Our `getCost` approach** — how we calculate cost, what fields we depend on, and any assumptions or estimates (especially when the API doesn't report exact dollar cost).
3. **API's rate limits** — the documented limits (RPM, TPM, concurrent, etc.), which plan tier they apply to, and whether they're per-key or per-account.
4. **Our `rateLimit` config** — what we set for `requestsPerMinute` (and dimensions if custom mode), why we chose those values, and how they compare to the API's limits.
5. **Key pooling impact** — how many hosted keys we expect, and how round-robin distribution affects the effective per-key rate at the API.
6. **Gaps or risks** — anything the API charges for that we don't meter, rate limit dimensions we chose not to enforce, or pricing that may be inaccurate due to variable model/tier costs.
### Format
Present this as a structured summary with clear headings. Example:
```
### Pricing
- **API charges**: $X per 1M tokens (input), $Y per 1M tokens (output) — varies by model
- **Response reports cost?**: No — only token counts in `usage` field
- **Our getCost**: Estimates cost at $Z per 1M total tokens based on median model pricing
- **Risk**: Actual cost varies by model; our estimate may over/undercharge for cheap/expensive models
### Throttling
- **API limits**: 300 RPM per key (paid tier), 60 RPM (free tier)
- **Per-key or per-account**: Per key — more keys = more throughput
- **Our config**: 60 RPM per workspace (per_request mode)
- **With N keys**: Effective per-key rate is (total RPM across workspaces) / N
- **Headroom**: Comfortable — even 10 active workspaces at full rate = 600 RPM / 3 keys = 200 RPM per key, under the 300 RPM API limit
```
This summary helps reviewers verify that the pricing and rate limiting are well-calibrated and surfaces any risks that need monitoring.
## Checklist
- [ ] Provider added to `BYOKProviderId` in `tools/types.ts`
- [ ] Provider added to `VALID_PROVIDERS` in the BYOK keys API route
- [ ] API pricing docs researched — understand per-unit cost and whether the API reports cost in responses
- [ ] API rate limits researched — understand RPM/TPM limits, per-key vs per-account, and plan tiers
- [ ] `hosting` config added to the tool with `envKeyPrefix`, `apiKeyParam`, `byokProviderId`, `pricing`, and `rateLimit`
- [ ] `getCost` throws if required cost data is missing from the response
- [ ] Cost data captured in `transformResponse` or `postProcess` if API provides it
- [ ] `hideWhenHosted: true` added to the API key subblock in the block config
- [ ] Provider entry added to the BYOK settings UI with icon and description
- [ ] Env vars documented: `{PREFIX}_COUNT` and `{PREFIX}_1..N`
- [ ] Pricing and throttling summary provided to reviewer

View File

@@ -1,4 +1,4 @@
FROM oven/bun:1.3.3-alpine
FROM oven/bun:1.3.10-alpine
# Install necessary packages for development
RUN apk add --no-cache \

View File

@@ -44,7 +44,7 @@ services:
deploy:
resources:
limits:
memory: 4G
memory: 1G
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:postgres@db:5432/simstudio

View File

@@ -8,7 +8,10 @@ on:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: false
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
test-build:
@@ -27,10 +30,11 @@ jobs:
steps:
- name: Extract version from commit message
id: extract
env:
COMMIT_MSG: ${{ github.event.head_commit.message }}
run: |
COMMIT_MSG="${{ github.event.head_commit.message }}"
# Only tag versions on main branch
if [ "${{ github.ref }}" = "refs/heads/main" ] && [[ "$COMMIT_MSG" =~ ^(v[0-9]+\.[0-9]+\.[0-9]+): ]]; then
if [ "$GITHUB_REF" = "refs/heads/main" ] && [[ "$COMMIT_MSG" =~ ^(v[0-9]+\.[0-9]+\.[0-9]+): ]]; then
VERSION="${BASH_REMATCH[1]}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "is_release=true" >> $GITHUB_OUTPUT
@@ -277,3 +281,30 @@ jobs:
if: needs.check-docs-changes.outputs.docs_changed == 'true'
uses: ./.github/workflows/docs-embeddings.yml
secrets: inherit
# Create GitHub Release (only for version commits on main, after all builds complete)
create-release:
name: Create GitHub Release
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: [create-ghcr-manifests, detect-version]
if: needs.detect-version.outputs.is_release == 'true'
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Create release
env:
GH_PAT: ${{ secrets.GITHUB_TOKEN }}
run: bun run scripts/create-single-release.ts ${{ needs.detect-version.outputs.version }}

View File

@@ -4,6 +4,9 @@ on:
workflow_call:
workflow_dispatch: # Allow manual triggering
permissions:
contents: read
jobs:
process-docs-embeddings:
name: Process Documentation Embeddings
@@ -17,7 +20,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.3
bun-version: 1.3.10
- name: Setup Node
uses: actions/setup-node@v4

View File

@@ -1,11 +1,7 @@
name: 'Auto-translate Documentation'
on:
push:
branches: [ staging ]
paths:
- 'apps/docs/content/docs/en/**'
- 'apps/docs/i18n.json'
workflow_dispatch: # Manual trigger only (scheduled runs disabled)
permissions:
contents: write
@@ -20,13 +16,14 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: staging
token: ${{ secrets.GH_PAT }}
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.3
bun-version: 1.3.10
- name: Cache Bun dependencies
uses: actions/cache@v4
@@ -68,12 +65,11 @@ jobs:
title: "feat(i18n): update translations"
body: |
## Summary
Automated translation updates triggered by changes to documentation.
This PR was automatically created after content changes were made, updating translations for all supported languages using Lingo.dev AI translation engine.
**Original trigger**: ${{ github.event.head_commit.message }}
**Commit**: ${{ github.sha }}
Automated weekly translation updates for documentation.
This PR was automatically created by the scheduled weekly i18n workflow, updating translations for all supported languages using Lingo.dev AI translation engine.
**Triggered**: Weekly scheduled run
**Workflow**: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
## Type of Change
@@ -107,7 +103,7 @@ jobs:
## Screenshots/Videos
<!-- Translation changes are text-based - no visual changes expected -->
<!-- Reviewers should check the documentation site renders correctly for all languages -->
branch: auto-translate/staging-merge-${{ github.run_id }}
branch: auto-translate/weekly-${{ github.run_id }}
base: staging
labels: |
i18n
@@ -126,7 +122,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.3
bun-version: 1.3.10
- name: Cache Bun dependencies
uses: actions/cache@v4
@@ -145,6 +141,8 @@ jobs:
bun install --frozen-lockfile
- name: Build documentation to verify translations
env:
DATABASE_URL: postgresql://dummy:dummy@localhost:5432/dummy
run: |
cd apps/docs
bun run build
@@ -153,7 +151,7 @@ jobs:
run: |
cd apps/docs
echo "## Translation Status Report" >> $GITHUB_STEP_SUMMARY
echo "**Triggered by merge to staging branch**" >> $GITHUB_STEP_SUMMARY
echo "**Weekly scheduled translation run**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
en_count=$(find content/docs/en -name "*.mdx" | wc -l)

View File

@@ -146,7 +146,7 @@ jobs:
create-ghcr-manifests:
name: Create GHCR Manifests
runs-on: blacksmith-8vcpu-ubuntu-2404
runs-on: blacksmith-2vcpu-ubuntu-2404
needs: [build-amd64, build-ghcr-arm64]
if: github.ref == 'refs/heads/main'
strategy:

View File

@@ -4,6 +4,9 @@ on:
workflow_call:
workflow_dispatch:
permissions:
contents: read
jobs:
migrate:
name: Apply Database Migrations
@@ -16,7 +19,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.3
bun-version: 1.3.10
- name: Cache Bun dependencies
uses: actions/cache@v4

View File

@@ -6,6 +6,9 @@ on:
paths:
- 'packages/cli/**'
permissions:
contents: read
jobs:
publish-npm:
runs-on: blacksmith-4vcpu-ubuntu-2404
@@ -16,7 +19,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.3
bun-version: 1.3.10
- name: Setup Node.js for npm publishing
uses: actions/setup-node@v4

View File

@@ -6,6 +6,9 @@ on:
paths:
- 'packages/python-sdk/**'
permissions:
contents: write
jobs:
publish-pypi:
runs-on: blacksmith-4vcpu-ubuntu-2404

View File

@@ -6,6 +6,9 @@ on:
paths:
- 'packages/ts-sdk/**'
permissions:
contents: write
jobs:
publish-npm:
runs-on: blacksmith-4vcpu-ubuntu-2404
@@ -16,7 +19,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.3
bun-version: 1.3.10
- name: Setup Node.js for npm publishing
uses: actions/setup-node@v4

View File

@@ -4,10 +4,13 @@ on:
workflow_call:
workflow_dispatch:
permissions:
contents: read
jobs:
test-build:
name: Test and Build
runs-on: blacksmith-4vcpu-ubuntu-2404
runs-on: blacksmith-8vcpu-ubuntu-2404
steps:
- name: Checkout code
@@ -16,27 +19,87 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.3
bun-version: 1.3.10
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: latest
- name: Cache Bun dependencies
- name: Mount Bun cache (Sticky Disk)
uses: useblacksmith/stickydisk@v1
with:
key: ${{ github.repository }}-bun-cache
path: ~/.bun/install/cache
- name: Mount node_modules (Sticky Disk)
uses: useblacksmith/stickydisk@v1
with:
key: ${{ github.repository }}-node-modules
path: ./node_modules
- name: Mount Turbo cache (Sticky Disk)
uses: useblacksmith/stickydisk@v1
with:
key: ${{ github.repository }}-turbo-cache
path: ./.turbo
- name: Restore Next.js build cache
uses: actions/cache@v4
with:
path: |
~/.bun/install/cache
node_modules
**/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
path: ./apps/sim/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-
${{ runner.os }}-nextjs-
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Validate feature flags
run: |
FILE="apps/sim/lib/core/config/feature-flags.ts"
ERRORS=""
echo "Checking for hardcoded boolean feature flags..."
# Use perl for multiline matching to catch both:
# export const isHosted = true
# export const isHosted =
# true
HARDCODED=$(perl -0777 -ne 'while (/export const (is[A-Za-z]+)\s*=\s*\n?\s*(true|false)\b/g) { print " $1 = $2\n" }' "$FILE")
if [ -n "$HARDCODED" ]; then
ERRORS="${ERRORS}\n❌ Feature flags must not be hardcoded to boolean literals!\n\nFound hardcoded flags:\n${HARDCODED}\n\nFeature flags should derive their values from environment variables.\n"
fi
echo "Checking feature flag naming conventions..."
# Check that all export const (except functions) start with 'is'
# This finds exports like "export const someFlag" that don't start with "is" or "get"
BAD_NAMES=$(grep -E "^export const [a-z]" "$FILE" | grep -vE "^export const (is|get)" | sed 's/export const \([a-zA-Z]*\).*/ \1/')
if [ -n "$BAD_NAMES" ]; then
ERRORS="${ERRORS}\n❌ Feature flags must use 'is' prefix for boolean flags!\n\nFound incorrectly named flags:\n${BAD_NAMES}\n\nExample: 'hostedMode' should be 'isHostedMode'\n"
fi
if [ -n "$ERRORS" ]; then
echo ""
echo -e "$ERRORS"
exit 1
fi
echo "✅ All feature flags are properly configured"
- name: Check subblock ID stability
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE_REF="origin/${{ github.base_ref }}"
git fetch --depth=1 origin "${{ github.base_ref }}" 2>/dev/null || true
else
BASE_REF="HEAD~1"
fi
bun run apps/sim/scripts/check-subblock-id-stability.ts "$BASE_REF"
- name: Lint code
run: bun run lint:check
@@ -46,6 +109,7 @@ jobs:
NEXT_PUBLIC_APP_URL: 'https://www.sim.ai'
DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/simstudio'
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
TURBO_CACHE_DIR: .turbo
run: bun run test
- name: Check schema and migrations are in sync
@@ -71,7 +135,8 @@ jobs:
RESEND_API_KEY: 'dummy_key_for_ci_only'
AWS_REGION: 'us-west-2'
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
run: bun run build
TURBO_CACHE_DIR: .turbo
run: bunx turbo run build --filter=sim
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5

7
.gitignore vendored
View File

@@ -26,6 +26,9 @@ bun-debug.log*
**/standalone/
sim-standalone.tar.gz
# redis
dump.rdb
# misc
.DS_Store
*.pem
@@ -73,3 +76,7 @@ start-collector.sh
## Helm Chart Tests
helm/sim/test
i18n.cache
## Claude Code
.claude/launch.json
.claude/worktrees/

392
CLAUDE.md
View File

@@ -1,47 +1,383 @@
# Expert Programming Standards
# Sim Development Guidelines
**You are tasked with implementing solutions that follow best practices. You MUST be accurate, elegant, and efficient as an expert programmer.**
You are a professional software engineer. All code must follow best practices: accurate, readable, clean, and efficient.
---
## Global Standards
# Role
- **Logging**: Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`
- **Comments**: Use TSDoc for documentation. No `====` separators. No non-TSDoc comments
- **Styling**: Never update global styles. Keep all styling local to components
- **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx`
You are a professional software engineer. All code you write MUST follow best practices, ensuring accuracy, quality, readability, and cleanliness. You MUST make FOCUSED EDITS that are EFFICIENT and ELEGANT.
## Architecture
## Logs
### Core Principles
1. Single Responsibility: Each component, hook, store has one clear purpose
2. Composition Over Complexity: Break down complex logic into smaller pieces
3. Type Safety First: TypeScript interfaces for all props, state, return types
4. Predictable State: Zustand for global state, useState for UI-only concerns
ENSURE that you use the logger.info and logger.warn and logger.error instead of the console.log whenever you want to display logs.
### Root Structure
```
apps/sim/
├── app/ # Next.js app router (pages, API routes)
├── blocks/ # Block definitions and registry
├── components/ # Shared UI (emcn/, ui/)
├── executor/ # Workflow execution engine
├── hooks/ # Shared hooks (queries/, selectors/)
├── lib/ # App-wide utilities
├── providers/ # LLM provider integrations
├── stores/ # Zustand stores
├── tools/ # Tool definitions
└── triggers/ # Trigger definitions
```
## Comments
### Naming Conventions
- Components: PascalCase (`WorkflowList`)
- Hooks: `use` prefix (`useWorkflowOperations`)
- Files: kebab-case (`workflow-list.tsx`)
- Stores: `stores/feature/store.ts`
- Constants: SCREAMING_SNAKE_CASE
- Interfaces: PascalCase with suffix (`WorkflowListProps`)
You must use TSDOC for comments. Do not use ==== for comments to separate sections. Do not leave any comments that are not TSDOC.
## Imports
## Global Styles
**Always use absolute imports.** Never use relative imports.
You should not update the global styles unless it is absolutely necessary. Keep all styling local to components and files.
```typescript
// ✓ Good
import { useWorkflowStore } from '@/stores/workflows/store'
## Bun
// ✗ Bad
import { useWorkflowStore } from '../../../stores/workflows/store'
```
Use bun and bunx not npm and npx.
Use barrel exports (`index.ts`) when a folder has 3+ exports. Do not re-export from non-barrel files; import directly from the source.
## Code Quality
### Import Order
1. React/core libraries
2. External libraries
3. UI components (`@/components/emcn`, `@/components/ui`)
4. Utilities (`@/lib/...`)
5. Stores (`@/stores/...`)
6. Feature imports
7. CSS imports
- Write clean, maintainable code that follows the project's existing patterns
- Prefer composition over inheritance
- Keep functions small and focused on a single responsibility
- Use meaningful variable and function names
- Handle errors gracefully and provide useful error messages
- Write type-safe code with proper TypeScript types
Use `import type { X }` for type-only imports.
## TypeScript
1. No `any` - Use proper types or `unknown` with type guards
2. Always define props interface for components
3. `as const` for constant objects/arrays
4. Explicit ref types: `useRef<HTMLDivElement>(null)`
## Components
```typescript
'use client' // Only if using hooks
const CONFIG = { SPACING: 8 } as const
interface ComponentProps {
requiredProp: string
optionalProp?: boolean
}
export function Component({ requiredProp, optionalProp = false }: ComponentProps) {
// Order: refs → external hooks → store hooks → custom hooks → state → useMemo → useCallback → useEffect → return
}
```
Extract when: 50+ lines, used in 2+ files, or has own state/logic. Keep inline when: < 10 lines, single use, purely presentational.
## Hooks
```typescript
interface UseFeatureProps { id: string }
export function useFeature({ id }: UseFeatureProps) {
const idRef = useRef(id)
const [data, setData] = useState<Data | null>(null)
useEffect(() => { idRef.current = id }, [id])
const fetchData = useCallback(async () => { ... }, []) // Empty deps when using refs
return { data, fetchData }
}
```
## Zustand Stores
Stores live in `stores/`. Complex stores split into `store.ts` + `types.ts`.
```typescript
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
const initialState = { items: [] as Item[] }
export const useFeatureStore = create<FeatureState>()(
devtools(
(set, get) => ({
...initialState,
setItems: (items) => set({ items }),
reset: () => set(initialState),
}),
{ name: 'feature-store' }
)
)
```
Use `devtools` middleware. Use `persist` only when data should survive reload with `partialize` to persist only necessary state.
## React Query
All React Query hooks live in `hooks/queries/`. All server state must go through React Query — never use `useState` + `fetch` in components for data fetching or mutations.
### Query Key Factory
Every file must have a hierarchical key factory with an `all` root key and intermediate plural keys for prefix invalidation:
```typescript
export const entityKeys = {
all: ['entity'] as const,
lists: () => [...entityKeys.all, 'list'] as const,
list: (workspaceId?: string) => [...entityKeys.lists(), workspaceId ?? ''] as const,
details: () => [...entityKeys.all, 'detail'] as const,
detail: (id?: string) => [...entityKeys.details(), id ?? ''] as const,
}
```
### Query Hooks
- Every `queryFn` must forward `signal` for request cancellation
- Every query must have an explicit `staleTime`
- Use `keepPreviousData` only on variable-key queries (where params change), never on static keys
```typescript
export function useEntityList(workspaceId?: string) {
return useQuery({
queryKey: entityKeys.list(workspaceId),
queryFn: ({ signal }) => fetchEntities(workspaceId as string, signal),
enabled: Boolean(workspaceId),
staleTime: 60 * 1000,
placeholderData: keepPreviousData, // OK: workspaceId varies
})
}
```
### Mutation Hooks
- Use targeted invalidation (`entityKeys.lists()`) not broad (`entityKeys.all`) when possible
- For optimistic updates: use `onSettled` (not `onSuccess`) for cache reconciliation — `onSettled` fires on both success and error
- Don't include mutation objects in `useCallback` deps — `.mutate()` is stable in TanStack Query v5
```typescript
export function useUpdateEntity() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (variables) => { /* ... */ },
onMutate: async (variables) => {
await queryClient.cancelQueries({ queryKey: entityKeys.detail(variables.id) })
const previous = queryClient.getQueryData(entityKeys.detail(variables.id))
queryClient.setQueryData(entityKeys.detail(variables.id), /* optimistic */)
return { previous }
},
onError: (_err, variables, context) => {
queryClient.setQueryData(entityKeys.detail(variables.id), context?.previous)
},
onSettled: (_data, _error, variables) => {
queryClient.invalidateQueries({ queryKey: entityKeys.lists() })
queryClient.invalidateQueries({ queryKey: entityKeys.detail(variables.id) })
},
})
}
```
## Styling
Use Tailwind only, no inline styles. Use `cn()` from `@/lib/utils` for conditional classes.
```typescript
<div className={cn('base-classes', isActive && 'active-classes')} />
```
## EMCN Components
Import from `@/components/emcn`, never from subpaths (except CSS files). Use CVA when 2+ variants exist.
## Testing
- Write tests for new functionality when appropriate
- Ensure existing tests pass before completing work
- Follow the project's testing conventions
Use Vitest. Test files: `feature.ts``feature.test.ts`. See `.cursor/rules/sim-testing.mdc` for full details.
## Performance
### Global Mocks (vitest.setup.ts)
- Consider performance implications of your code
- Avoid unnecessary re-renders in React components
- Use appropriate data structures and algorithms
- Profile and optimize when necessary
`@sim/db`, `drizzle-orm`, `@sim/logger`, `@/blocks/registry`, `@trigger.dev/sdk`, and store mocks are provided globally. Do NOT re-mock them unless overriding behavior.
### Standard Test Pattern
```typescript
/**
* @vitest-environment node
*/
import { createMockRequest } from '@sim/testing'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const { mockGetSession } = vi.hoisted(() => ({
mockGetSession: vi.fn(),
}))
vi.mock('@/lib/auth', () => ({
auth: { api: { getSession: vi.fn() } },
getSession: mockGetSession,
}))
import { GET } from '@/app/api/my-route/route'
describe('my route', () => {
beforeEach(() => {
vi.clearAllMocks()
mockGetSession.mockResolvedValue({ user: { id: 'user-1' } })
})
it('returns data', async () => { ... })
})
```
### Performance Rules
- **NEVER** use `vi.resetModules()` + `vi.doMock()` + `await import()` — use `vi.hoisted()` + `vi.mock()` + static imports
- **NEVER** use `vi.importActual()` — mock everything explicitly
- **NEVER** use `mockAuth()`, `mockConsoleLogger()`, `setupCommonApiMocks()` from `@sim/testing` — they use `vi.doMock()` internally
- **Mock heavy deps** (`@/blocks`, `@/tools/registry`, `@/triggers`) in tests that don't need them
- **Use `@vitest-environment node`** unless DOM APIs are needed (`window`, `document`, `FormData`)
- **Avoid real timers** — use 1ms delays or `vi.useFakeTimers()`
Use `@sim/testing` mocks/factories over local test data.
## Utils Rules
- Never create `utils.ts` for single consumer - inline it
- Create `utils.ts` when 2+ files need the same helper
- Check existing sources in `lib/` before duplicating
## Adding Integrations
New integrations require: **Tools****Block****Icon** → (optional) **Trigger**
Always look up the service's API docs first.
### 1. Tools (`tools/{service}/`)
```
tools/{service}/
├── index.ts # Barrel export
├── types.ts # Params/response types
└── {action}.ts # Tool implementation
```
**Tool structure:**
```typescript
export const serviceTool: ToolConfig<Params, Response> = {
id: 'service_action',
name: 'Service Action',
description: '...',
version: '1.0.0',
oauth: { required: true, provider: 'service' },
params: { /* ... */ },
request: { url: '/api/tools/service/action', method: 'POST', ... },
transformResponse: async (response) => { /* ... */ },
outputs: { /* ... */ },
}
```
Register in `tools/registry.ts`.
### 2. Block (`blocks/blocks/{service}.ts`)
```typescript
export const ServiceBlock: BlockConfig = {
type: 'service',
name: 'Service',
description: '...',
category: 'tools',
bgColor: '#hexcolor',
icon: ServiceIcon,
subBlocks: [ /* see SubBlock Properties */ ],
tools: { access: ['service_action'], config: { tool: (p) => `service_${p.operation}`, params: (p) => ({ /* type coercions here */ }) } },
inputs: { /* ... */ },
outputs: { /* ... */ },
}
```
Register in `blocks/registry.ts` (alphabetically).
**Important:** `tools.config.tool` runs during serialization (before variable resolution). Never do `Number()` or other type coercions there — dynamic references like `<Block.output>` will be destroyed. Use `tools.config.params` for type coercions (it runs during execution, after variables are resolved).
**SubBlock Properties:**
```typescript
{
id: 'field', title: 'Label', type: 'short-input', placeholder: '...',
required: true, // or condition object
condition: { field: 'op', value: 'send' }, // show/hide
dependsOn: ['credential'], // clear when dep changes
mode: 'basic', // 'basic' | 'advanced' | 'both' | 'trigger'
}
```
**condition examples:**
- `{ field: 'op', value: 'send' }` - show when op === 'send'
- `{ field: 'op', value: ['a','b'] }` - show when op is 'a' OR 'b'
- `{ field: 'op', value: 'x', not: true }` - show when op !== 'x'
- `{ field: 'op', value: 'x', not: true, and: { field: 'type', value: 'dm', not: true } }` - complex
**dependsOn:** `['field']` or `{ all: ['a'], any: ['b', 'c'] }`
**File Input Pattern (basic/advanced mode):**
```typescript
// Basic: file-upload UI
{ id: 'uploadFile', type: 'file-upload', canonicalParamId: 'file', mode: 'basic' },
// Advanced: reference from other blocks
{ id: 'fileRef', type: 'short-input', canonicalParamId: 'file', mode: 'advanced' },
```
In `tools.config.tool`, normalize with:
```typescript
import { normalizeFileInput } from '@/blocks/utils'
const file = normalizeFileInput(params.uploadFile || params.fileRef, { single: true })
if (file) params.file = file
```
For file uploads, create an internal API route (`/api/tools/{service}/upload`) that uses `downloadFileFromStorage` to get file content from `UserFile` objects.
### 3. Icon (`components/icons.tsx`)
```typescript
export function ServiceIcon(props: SVGProps<SVGSVGElement>) {
return <svg {...props}>/* SVG from brand assets */</svg>
}
```
### 4. Trigger (`triggers/{service}/`) - Optional
```
triggers/{service}/
├── index.ts # Barrel export
├── webhook.ts # Webhook handler
└── {event}.ts # Event-specific handlers
```
Register in `triggers/registry.ts`.
### Integration Checklist
- [ ] Look up API docs
- [ ] Create `tools/{service}/` with types and tools
- [ ] Register tools in `tools/registry.ts`
- [ ] Add icon to `components/icons.tsx`
- [ ] Create block in `blocks/blocks/{service}.ts`
- [ ] Register block in `blocks/registry.ts`
- [ ] (Optional) Create and register triggers
- [ ] (If file uploads) Create internal API route with `downloadFileFromStorage`
- [ ] (If file uploads) Use `normalizeFileInput` in block config

View File

@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2025 Sim Studio, Inc.
Copyright 2026 Sim Studio, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

2
NOTICE
View File

@@ -1,4 +1,4 @@
Sim Studio
Copyright 2025 Sim Studio
Copyright 2026 Sim Studio
This product includes software developed for the Sim project.

146
README.md
View File

@@ -4,15 +4,19 @@
</a>
</p>
<p align="center">Build and deploy AI agent workflows in minutes.</p>
<p align="center">The open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to orchestrate agentic workflows.</p>
<p align="center">
<a href="https://sim.ai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/sim.ai-6F3DFA" alt="Sim.ai"></a>
<a href="https://discord.gg/Hr4UWYEcTT" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white" alt="Discord"></a>
<a href="https://x.com/simdotai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/twitter/follow/simstudioai?style=social" alt="Twitter"></a>
<a href="https://x.com/simdotai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/twitter/follow/simdotai?style=social" alt="Twitter"></a>
<a href="https://docs.sim.ai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Docs-6F3DFA.svg" alt="Documentation"></a>
</p>
<p align="center">
<a href="https://deepwiki.com/simstudioai/sim" target="_blank" rel="noopener noreferrer"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a> <a href="https://cursor.com/link/prompt?text=Help%20me%20set%20up%20Sim%20locally.%20Follow%20these%20steps%3A%0A%0A1.%20First%2C%20verify%20Docker%20is%20installed%20and%20running%3A%0A%20%20%20docker%20--version%0A%20%20%20docker%20info%0A%0A2.%20Clone%20the%20repository%3A%0A%20%20%20git%20clone%20https%3A%2F%2Fgithub.com%2Fsimstudioai%2Fsim.git%0A%20%20%20cd%20sim%0A%0A3.%20Start%20the%20services%20with%20Docker%20Compose%3A%0A%20%20%20docker%20compose%20-f%20docker-compose.prod.yml%20up%20-d%0A%0A4.%20Wait%20for%20all%20containers%20to%20be%20healthy%20(this%20may%20take%201-2%20minutes)%3A%0A%20%20%20docker%20compose%20-f%20docker-compose.prod.yml%20ps%0A%0A5.%20Verify%20the%20app%20is%20accessible%20at%20http%3A%2F%2Flocalhost%3A3000%0A%0AIf%20there%20are%20any%20errors%2C%20help%20me%20troubleshoot%20them.%20Common%20issues%3A%0A-%20Port%203000%2C%203002%2C%20or%205432%20already%20in%20use%0A-%20Docker%20not%20running%0A-%20Insufficient%20memory%20(needs%2012GB%2B%20RAM)%0A%0AFor%20local%20AI%20models%20with%20Ollama%2C%20use%20this%20instead%20of%20step%203%3A%0A%20%20%20docker%20compose%20-f%20docker-compose.ollama.yml%20--profile%20setup%20up%20-d"><img src="https://img.shields.io/badge/Set%20Up%20with-Cursor-000000?logo=cursor&logoColor=white" alt="Set Up with Cursor"></a>
</p>
### Build Workflows with Ease
Design agent workflows visually on a canvas—connect agents, tools, and blocks, then run them instantly.
@@ -60,17 +64,11 @@ Docker must be installed and running on your machine.
### Self-hosted: Docker Compose
```bash
# Clone the repository
git clone https://github.com/simstudioai/sim.git
# Navigate to the project directory
cd sim
# Start Sim
git clone https://github.com/simstudioai/sim.git && cd sim
docker compose -f docker-compose.prod.yml up -d
```
Access the application at [http://localhost:3000/](http://localhost:3000/)
Open [http://localhost:3000](http://localhost:3000)
#### Using Local Models with Ollama
@@ -91,33 +89,17 @@ docker compose -f docker-compose.ollama.yml exec ollama ollama pull llama3.1:8b
#### Using an External Ollama Instance
If you already have Ollama running on your host machine (outside Docker), you need to configure the `OLLAMA_URL` to use `host.docker.internal` instead of `localhost`:
If Ollama is running on your host machine, use `host.docker.internal` instead of `localhost`:
```bash
# Docker Desktop (macOS/Windows)
OLLAMA_URL=http://host.docker.internal:11434 docker compose -f docker-compose.prod.yml up -d
# Linux (add extra_hosts or use host IP)
docker compose -f docker-compose.prod.yml up -d # Then set OLLAMA_URL to your host's IP
```
**Why?** When running inside Docker, `localhost` refers to the container itself, not your host machine. `host.docker.internal` is a special DNS name that resolves to the host.
For Linux users, you can either:
- Use your host machine's actual IP address (e.g., `http://192.168.1.100:11434`)
- Add `extra_hosts: ["host.docker.internal:host-gateway"]` to the simstudio service in your compose file
On Linux, use your host's IP address or add `extra_hosts: ["host.docker.internal:host-gateway"]` to the compose file.
#### Using vLLM
Sim also supports [vLLM](https://docs.vllm.ai/) for self-hosted models with OpenAI-compatible API:
```bash
# Set these environment variables
VLLM_BASE_URL=http://your-vllm-server:8000
VLLM_API_KEY=your_optional_api_key # Only if your vLLM instance requires auth
```
When running with Docker, use `host.docker.internal` if vLLM is on your host machine (same as Ollama above).
Sim supports [vLLM](https://docs.vllm.ai/) for self-hosted models. Set `VLLM_BASE_URL` and optionally `VLLM_API_KEY` in your environment.
### Self-hosted: Dev Containers
@@ -128,14 +110,9 @@ When running with Docker, use `host.docker.internal` if vLLM is on your host mac
### Self-hosted: Manual Setup
**Requirements:**
- [Bun](https://bun.sh/) runtime
- [Node.js](https://nodejs.org/) v20+ (required for sandboxed code execution)
- PostgreSQL 12+ with [pgvector extension](https://github.com/pgvector/pgvector) (required for AI embeddings)
**Requirements:** [Bun](https://bun.sh/), [Node.js](https://nodejs.org/) v20+, PostgreSQL 12+ with [pgvector](https://github.com/pgvector/pgvector)
**Note:** Sim uses vector embeddings for AI features like knowledge bases and semantic search, which requires the `pgvector` PostgreSQL extension.
1. Clone and install dependencies:
1. Clone and install:
```bash
git clone https://github.com/simstudioai/sim.git
@@ -145,75 +122,33 @@ bun install
2. Set up PostgreSQL with pgvector:
You need PostgreSQL with the `vector` extension for embedding support. Choose one option:
**Option A: Using Docker (Recommended)**
```bash
# Start PostgreSQL with pgvector extension
docker run --name simstudio-db \
-e POSTGRES_PASSWORD=your_password \
-e POSTGRES_DB=simstudio \
-p 5432:5432 -d \
pgvector/pgvector:pg17
docker run --name simstudio-db -e POSTGRES_PASSWORD=your_password -e POSTGRES_DB=simstudio -p 5432:5432 -d pgvector/pgvector:pg17
```
**Option B: Manual Installation**
- Install PostgreSQL 12+ and the pgvector extension
- See [pgvector installation guide](https://github.com/pgvector/pgvector#installation)
Or install manually via the [pgvector guide](https://github.com/pgvector/pgvector#installation).
3. Set up environment:
3. Configure environment:
```bash
cd apps/sim
cp .env.example .env # Configure with required variables (DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL)
cp apps/sim/.env.example apps/sim/.env
cp packages/db/.env.example packages/db/.env
# Edit both .env files to set DATABASE_URL="postgresql://postgres:your_password@localhost:5432/simstudio"
```
Update your `.env` file with the database URL:
```bash
DATABASE_URL="postgresql://postgres:your_password@localhost:5432/simstudio"
```
4. Set up the database:
First, configure the database package environment:
```bash
cd packages/db
cp .env.example .env
```
Update your `packages/db/.env` file with the database URL:
```bash
DATABASE_URL="postgresql://postgres:your_password@localhost:5432/simstudio"
```
Then run the migrations:
```bash
cd packages/db # Required so drizzle picks correct .env file
bunx drizzle-kit migrate --config=./drizzle.config.ts
```
5. Start the development servers:
**Recommended approach - run both servers together (from project root):**
4. Run migrations:
```bash
bun run dev:full
cd packages/db && bunx drizzle-kit migrate --config=./drizzle.config.ts
```
This starts both the main Next.js application and the realtime socket server required for full functionality.
5. Start development servers:
**Alternative - run servers separately:**
Next.js app (from project root):
```bash
bun run dev
bun run dev:full # Starts both Next.js app and realtime socket server
```
Realtime socket server (from `apps/sim` directory in a separate terminal):
```bash
cd apps/sim
bun run dev:sockets
```
Or run separately: `bun run dev` (Next.js) and `cd apps/sim && bun run dev:sockets` (realtime).
## Copilot API Keys
@@ -224,7 +159,7 @@ Copilot is a Sim-managed service. To use Copilot on a self-hosted instance:
## Environment Variables
Key environment variables for self-hosted deployments (see `apps/sim/.env.example` for full list):
Key environment variables for self-hosted deployments. See [`.env.example`](apps/sim/.env.example) for defaults or [`env.ts`](apps/sim/lib/core/config/env.ts) for the full list.
| Variable | Required | Description |
|----------|----------|-------------|
@@ -232,36 +167,11 @@ Key environment variables for self-hosted deployments (see `apps/sim/.env.exampl
| `BETTER_AUTH_SECRET` | Yes | Auth secret (`openssl rand -hex 32`) |
| `BETTER_AUTH_URL` | Yes | Your app URL (e.g., `http://localhost:3000`) |
| `NEXT_PUBLIC_APP_URL` | Yes | Public app URL (same as above) |
| `ENCRYPTION_KEY` | Yes | Encryption key (`openssl rand -hex 32`) |
| `OLLAMA_URL` | No | Ollama server URL (default: `http://localhost:11434`) |
| `VLLM_BASE_URL` | No | vLLM server URL for self-hosted models |
| `ENCRYPTION_KEY` | Yes | Encrypts environment variables (`openssl rand -hex 32`) |
| `INTERNAL_API_SECRET` | Yes | Encrypts internal API routes (`openssl rand -hex 32`) |
| `API_ENCRYPTION_KEY` | Yes | Encrypts API keys (`openssl rand -hex 32`) |
| `COPILOT_API_KEY` | No | API key from sim.ai for Copilot features |
## Troubleshooting
### Ollama models not showing in dropdown (Docker)
If you're running Ollama on your host machine and Sim in Docker, change `OLLAMA_URL` from `localhost` to `host.docker.internal`:
```bash
OLLAMA_URL=http://host.docker.internal:11434 docker compose -f docker-compose.prod.yml up -d
```
See [Using an External Ollama Instance](#using-an-external-ollama-instance) for details.
### Database connection issues
Ensure PostgreSQL has the pgvector extension installed. When using Docker, wait for the database to be healthy before running migrations.
### Port conflicts
If ports 3000, 3002, or 5432 are in use, configure alternatives:
```bash
# Custom ports
NEXT_PUBLIC_APP_URL=http://localhost:3100 POSTGRES_PORT=5433 docker compose up -d
```
## Tech Stack
- **Framework**: [Next.js](https://nextjs.org/) (App Router)

View File

@@ -1,5 +1,9 @@
import type React from 'react'
import type { Root } from 'fumadocs-core/page-tree'
import { findNeighbour } from 'fumadocs-core/page-tree'
import type { ApiPageProps } from 'fumadocs-openapi/ui'
import { createAPIPage } from 'fumadocs-openapi/ui'
import { Pre } from 'fumadocs-ui/components/codeblock'
import defaultMdxComponents from 'fumadocs-ui/mdx'
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/page'
import { ChevronLeft, ChevronRight } from 'lucide-react'
@@ -11,27 +15,75 @@ import { LLMCopyButton } from '@/components/page-actions'
import { StructuredData } from '@/components/structured-data'
import { CodeBlock } from '@/components/ui/code-block'
import { Heading } from '@/components/ui/heading'
import { ResponseSection } from '@/components/ui/response-section'
import { i18n } from '@/lib/i18n'
import { getApiSpecContent, openapi } from '@/lib/openapi'
import { type PageData, source } from '@/lib/source'
const SUPPORTED_LANGUAGES: Set<string> = new Set(i18n.languages)
const BASE_URL = 'https://docs.sim.ai'
function resolveLangAndSlug(params: { slug?: string[]; lang: string }) {
const isValidLang = SUPPORTED_LANGUAGES.has(params.lang)
const lang = isValidLang ? params.lang : 'en'
const slug = isValidLang ? params.slug : [params.lang, ...(params.slug ?? [])]
return { lang, slug }
}
const APIPage = createAPIPage(openapi, {
playground: { enabled: false },
content: {
renderOperationLayout: async (slots) => {
return (
<div className='flex @4xl:flex-row flex-col @4xl:items-start gap-x-6 gap-y-4'>
<div className='min-w-0 flex-1'>
{slots.header}
{slots.apiPlayground}
{slots.authSchemes && <div className='api-section-divider'>{slots.authSchemes}</div>}
{slots.paremeters}
{slots.body && <div className='api-section-divider'>{slots.body}</div>}
<ResponseSection>{slots.responses}</ResponseSection>
{slots.callbacks}
</div>
<div className='@4xl:sticky @4xl:top-[calc(var(--fd-docs-row-1,2rem)+1rem)] @4xl:w-[400px]'>
{slots.apiExample}
</div>
</div>
)
},
},
})
export default async function Page(props: { params: Promise<{ slug?: string[]; lang: string }> }) {
const params = await props.params
const page = source.getPage(params.slug, params.lang)
const { lang, slug } = resolveLangAndSlug(params)
const page = source.getPage(slug, lang)
if (!page) notFound()
const data = page.data as PageData
const MDX = data.body
const baseUrl = 'https://docs.sim.ai'
const data = page.data as unknown as PageData & {
_openapi?: { method?: string }
getAPIPageProps?: () => ApiPageProps
}
const isOpenAPI = '_openapi' in data && data._openapi != null
const isApiReference = slug?.some((s) => s === 'api-reference') ?? false
const pageTreeRecord = source.pageTree as Record<string, any>
const pageTree =
pageTreeRecord[params.lang] ?? pageTreeRecord.en ?? Object.values(pageTreeRecord)[0]
const neighbours = pageTree ? findNeighbour(pageTree, page.url) : null
const pageTreeRecord = source.pageTree as Record<string, Root>
const pageTree = pageTreeRecord[lang] ?? pageTreeRecord.en ?? Object.values(pageTreeRecord)[0]
const rawNeighbours = pageTree ? findNeighbour(pageTree, page.url) : null
const neighbours = isApiReference
? {
previous: rawNeighbours?.previous?.url.includes('/api-reference/')
? rawNeighbours.previous
: undefined,
next: rawNeighbours?.next?.url.includes('/api-reference/') ? rawNeighbours.next : undefined,
}
: rawNeighbours
const generateBreadcrumbs = () => {
const breadcrumbs: Array<{ name: string; url: string }> = [
{
name: 'Home',
url: baseUrl,
url: BASE_URL,
},
]
@@ -39,7 +91,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
let currentPath = ''
urlParts.forEach((part, index) => {
if (index === 0 && ['en', 'es', 'fr', 'de', 'ja', 'zh'].includes(part)) {
if (index === 0 && SUPPORTED_LANGUAGES.has(part)) {
currentPath = `/${part}`
return
}
@@ -54,12 +106,12 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
if (index === urlParts.length - 1) {
breadcrumbs.push({
name: data.title,
url: `${baseUrl}${page.url}`,
url: `${BASE_URL}${page.url}`,
})
} else {
breadcrumbs.push({
name: name,
url: `${baseUrl}${currentPath}`,
url: `${BASE_URL}${currentPath}`,
})
}
})
@@ -71,7 +123,6 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
const CustomFooter = () => (
<div className='mt-12'>
{/* Navigation links */}
<div className='flex items-center justify-between py-8'>
{neighbours?.previous ? (
<Link
@@ -98,10 +149,8 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
)}
</div>
{/* Divider line */}
<div className='border-border border-t' />
{/* Social icons */}
<div className='flex items-center gap-4 py-6'>
<Link
href='https://x.com/simdotai'
@@ -167,13 +216,70 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
</div>
)
if (isOpenAPI && data.getAPIPageProps) {
const apiProps = data.getAPIPageProps()
const apiPageContent = getApiSpecContent(
data.title,
data.description,
apiProps.operations ?? []
)
return (
<>
<StructuredData
title={data.title}
description={data.description || ''}
url={`${BASE_URL}${page.url}`}
lang={lang}
breadcrumb={breadcrumbs}
/>
<style>{`#nd-page { grid-column: main-start / toc-end !important; max-width: 1400px !important; }`}</style>
<DocsPage
toc={data.toc}
breadcrumb={{
enabled: false,
}}
tableOfContent={{
style: 'clerk',
enabled: false,
}}
tableOfContentPopover={{
style: 'clerk',
enabled: false,
}}
footer={{
enabled: true,
component: <CustomFooter />,
}}
>
<div className='api-page-header relative mt-6 sm:mt-0'>
<div className='absolute top-1 right-0 flex items-center gap-2'>
<div className='hidden sm:flex'>
<LLMCopyButton content={apiPageContent} />
</div>
<PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} />
</div>
<DocsTitle>{data.title}</DocsTitle>
<DocsDescription>{data.description}</DocsDescription>
</div>
<DocsBody>
<APIPage {...apiProps} />
</DocsBody>
</DocsPage>
</>
)
}
const MDX = data.body
const markdownContent = await data.getText('processed')
return (
<>
<StructuredData
title={data.title}
description={data.description || ''}
url={`${baseUrl}${page.url}`}
lang={params.lang}
url={`${BASE_URL}${page.url}`}
lang={lang}
breadcrumb={breadcrumbs}
/>
<DocsPage
@@ -185,11 +291,6 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
tableOfContent={{
style: 'clerk',
enabled: true,
header: (
<div key='toc-header' className='mb-2 font-medium text-sm'>
On this page
</div>
),
footer: <TOCFooter />,
single: false,
}}
@@ -205,7 +306,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
<div className='relative mt-6 sm:mt-0'>
<div className='absolute top-1 right-0 flex items-center gap-2'>
<div className='hidden sm:flex'>
<LLMCopyButton markdownUrl={`${page.url}.mdx`} />
<LLMCopyButton content={markdownContent} />
</div>
<PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} />
</div>
@@ -216,7 +317,11 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
<MDX
components={{
...defaultMdxComponents,
CodeBlock,
pre: (props: React.HTMLAttributes<HTMLPreElement>) => (
<CodeBlock {...props}>
<Pre>{props.children}</Pre>
</CodeBlock>
),
h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
<Heading as='h1' {...props} />
),
@@ -251,28 +356,29 @@ export async function generateMetadata(props: {
params: Promise<{ slug?: string[]; lang: string }>
}) {
const params = await props.params
const page = source.getPage(params.slug, params.lang)
const { lang, slug } = resolveLangAndSlug(params)
const page = source.getPage(slug, lang)
if (!page) notFound()
const data = page.data as PageData
const baseUrl = 'https://docs.sim.ai'
const fullUrl = `${baseUrl}${page.url}`
const data = page.data as unknown as PageData
const fullUrl = `${BASE_URL}${page.url}`
const description = data.description || ''
const ogImageUrl = `${baseUrl}/api/og?title=${encodeURIComponent(data.title)}&category=DOCUMENTATION${description ? `&description=${encodeURIComponent(description)}` : ''}`
const ogImageUrl = `${BASE_URL}/api/og?title=${encodeURIComponent(data.title)}`
return {
title: data.title,
description:
data.description || 'Sim visual workflow builder for AI applications documentation',
data.description ||
'Documentation for Sim — the open-source platform to build AI agents and run your agentic workforce.',
keywords: [
'AI workflow builder',
'visual workflow editor',
'AI automation',
'workflow automation',
'AI agents',
'no-code AI',
'drag and drop workflows',
'agentic workforce',
'AI agent platform',
'agentic workflows',
'LLM orchestration',
'AI automation',
'knowledge base',
'AI integrations',
data.title?.toLowerCase().split(' '),
]
.flat()
@@ -282,14 +388,15 @@ export async function generateMetadata(props: {
openGraph: {
title: data.title,
description:
data.description || 'Sim visual workflow builder for AI applications documentation',
data.description ||
'Documentation for Sim — the open-source platform to build AI agents and run your agentic workforce.',
url: fullUrl,
siteName: 'Sim Documentation',
type: 'article',
locale: params.lang === 'en' ? 'en_US' : `${params.lang}_${params.lang.toUpperCase()}`,
locale: lang === 'en' ? 'en_US' : `${lang}_${lang.toUpperCase()}`,
alternateLocale: ['en', 'es', 'fr', 'de', 'ja', 'zh']
.filter((lang) => lang !== params.lang)
.map((lang) => (lang === 'en' ? 'en_US' : `${lang}_${lang.toUpperCase()}`)),
.filter((l) => l !== lang)
.map((l) => (l === 'en' ? 'en_US' : `${l}_${l.toUpperCase()}`)),
images: [
{
url: ogImageUrl,
@@ -303,7 +410,8 @@ export async function generateMetadata(props: {
card: 'summary_large_image',
title: data.title,
description:
data.description || 'Sim visual workflow builder for AI applications documentation',
data.description ||
'Documentation for Sim — the open-source platform to build AI agents and run your agentic workforce.',
images: [ogImageUrl],
creator: '@simdotai',
site: '@simdotai',
@@ -323,13 +431,13 @@ export async function generateMetadata(props: {
alternates: {
canonical: fullUrl,
languages: {
'x-default': `${baseUrl}${page.url.replace(`/${params.lang}`, '')}`,
en: `${baseUrl}${page.url.replace(`/${params.lang}`, '')}`,
es: `${baseUrl}/es${page.url.replace(`/${params.lang}`, '')}`,
fr: `${baseUrl}/fr${page.url.replace(`/${params.lang}`, '')}`,
de: `${baseUrl}/de${page.url.replace(`/${params.lang}`, '')}`,
ja: `${baseUrl}/ja${page.url.replace(`/${params.lang}`, '')}`,
zh: `${baseUrl}/zh${page.url.replace(`/${params.lang}`, '')}`,
'x-default': `${BASE_URL}${page.url.replace(`/${lang}`, '')}`,
en: `${BASE_URL}${page.url.replace(`/${lang}`, '')}`,
es: `${BASE_URL}/es${page.url.replace(`/${lang}`, '')}`,
fr: `${BASE_URL}/fr${page.url.replace(`/${lang}`, '')}`,
de: `${BASE_URL}/de${page.url.replace(`/${lang}`, '')}`,
ja: `${BASE_URL}/ja${page.url.replace(`/${lang}`, '')}`,
zh: `${BASE_URL}/zh${page.url.replace(`/${lang}`, '')}`,
},
},
}

View File

@@ -3,13 +3,15 @@ import { defineI18nUI } from 'fumadocs-ui/i18n'
import { DocsLayout } from 'fumadocs-ui/layouts/docs'
import { RootProvider } from 'fumadocs-ui/provider/next'
import { Geist_Mono, Inter } from 'next/font/google'
import Image from 'next/image'
import Script from 'next/script'
import {
SidebarFolder,
SidebarItem,
SidebarSeparator,
} from '@/components/docs-layout/sidebar-components'
import { Navbar } from '@/components/navbar/navbar'
import { AnimatedBlocks } from '@/components/ui/animated-blocks'
import { SimLogoFull } from '@/components/ui/sim-logo'
import { i18n } from '@/lib/i18n'
import { source } from '@/lib/source'
import '../global.css'
@@ -17,11 +19,13 @@ import '../global.css'
const inter = Inter({
subsets: ['latin'],
variable: '--font-geist-sans',
display: 'swap',
})
const geistMono = Geist_Mono({
subsets: ['latin'],
variable: '--font-geist-mono',
display: 'swap',
})
const { provider } = defineI18nUI(i18n, {
@@ -52,15 +56,18 @@ type LayoutProps = {
params: Promise<{ lang: string }>
}
const SUPPORTED_LANGUAGES: Set<string> = new Set(i18n.languages)
export default async function Layout({ children, params }: LayoutProps) {
const { lang } = await params
const { lang: rawLang } = await params
const lang = SUPPORTED_LANGUAGES.has(rawLang) ? rawLang : 'en'
const structuredData = {
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'Sim Documentation',
description:
'Comprehensive documentation for Sim - the visual workflow builder for AI Agent Workflows.',
'Documentation for Sim the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows.',
url: 'https://docs.sim.ai',
publisher: {
'@type': 'Organization',
@@ -93,27 +100,19 @@ export default async function Layout({ children, params }: LayoutProps) {
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
{/* OneDollarStats Analytics - CDN script handles everything automatically */}
<script defer src='https://assets.onedollarstats.com/stonks.js' />
</head>
<body className='flex min-h-screen flex-col font-sans'>
<Script src='https://assets.onedollarstats.com/stonks.js' strategy='lazyOnload' />
<AnimatedBlocks />
<RootProvider i18n={provider(lang)}>
<Navbar />
<DocsLayout
tree={source.pageTree[lang]}
nav={{
title: (
<Image
src='/static/logo.png'
alt='Sim'
width={72}
height={28}
className='h-7 w-auto'
priority
/>
),
title: <SimLogoFull className='h-7 w-auto' />,
}}
sidebar={{
tabs: false,
defaultOpenLevel: 0,
collapsible: false,
footer: null,

View File

@@ -9,7 +9,7 @@ export default function NotFound() {
<DocsPage>
<DocsBody>
<div className='flex min-h-[60vh] flex-col items-center justify-center text-center'>
<h1 className='mb-4 bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] bg-clip-text font-bold text-8xl text-transparent'>
<h1 className='mb-4 bg-gradient-to-b from-[#47d991] to-[#33c482] bg-clip-text font-bold text-8xl text-transparent'>
404
</h1>
<h2 className='mb-2 font-semibold text-2xl text-foreground'>Page Not Found</h2>

View File

@@ -33,18 +33,42 @@ async function loadGoogleFont(font: string, weights: string, text: string): Prom
throw new Error('Failed to load font data')
}
/**
* Sim logo with icon and "Sim" text for OG image.
*/
function SimLogoFull() {
return (
<svg height='28' viewBox='720 440 1020 320' fill='none'>
{/* Green icon - top left shape with cutout */}
<path
fillRule='evenodd'
clipRule='evenodd'
d='M875.791 577.171C875.791 581.922 873.911 586.483 870.576 589.842L870.098 590.323C866.764 593.692 862.234 595.575 857.517 595.575H750.806C740.978 595.575 733 603.6 733 613.498V728.902C733 738.799 740.978 746.826 750.806 746.826H865.382C875.209 746.826 883.177 738.799 883.177 728.902V620.853C883.177 616.448 884.912 612.222 888.008 609.104C891.093 605.997 895.29 604.249 899.664 604.249H1008.16C1017.99 604.249 1025.96 596.224 1025.96 586.327V470.923C1025.96 461.025 1017.99 453 1008.16 453H893.586C883.759 453 875.791 461.025 875.791 470.923V577.171ZM910.562 477.566H991.178C996.922 477.566 1001.57 482.254 1001.57 488.029V569.22C1001.57 574.995 996.922 579.683 991.178 579.683H910.562C904.828 579.683 900.173 574.995 900.173 569.22V488.029C900.173 482.254 904.828 477.566 910.562 477.566Z'
fill='#33C482'
/>
{/* Green icon - bottom right square */}
<path
d='M1008.3 624.59H923.113C912.786 624.59 904.414 633.022 904.414 643.423V728.171C904.414 738.572 912.786 747.004 923.113 747.004H1008.3C1018.63 747.004 1027 738.572 1027 728.171V643.423C1027 633.022 1018.63 624.59 1008.3 624.59Z'
fill='#33C482'
/>
{/* "Sim" text - white for dark background */}
<path
d='M1210.54 515.657C1226.65 515.657 1240.59 518.51 1252.31 524.257H1252.31C1264.3 529.995 1273.63 538.014 1280.26 548.319H1280.26C1287.19 558.635 1290.78 570.899 1291.08 585.068L1291.1 586.089H1249.11L1249.09 585.115C1248.8 574.003 1245.18 565.493 1238.32 559.451C1231.45 553.399 1221.79 550.308 1209.21 550.308C1196.3 550.308 1186.48 553.113 1179.61 558.588C1172.76 564.046 1169.33 571.499 1169.33 581.063C1169.33 588.092 1171.88 593.978 1177.01 598.783C1182.17 603.618 1189.99 607.399 1200.56 610.061H1200.56L1238.77 619.451C1257.24 623.65 1271.21 630.571 1280.57 640.293L1281.01 640.739C1290.13 650.171 1294.64 662.97 1294.64 679.016C1294.64 692.923 1290.88 705.205 1283.34 715.822L1283.33 715.834C1275.81 726.134 1265.44 734.14 1252.26 739.866L1252.25 739.871C1239.36 745.302 1224.12 748 1206.54 748C1180.9 748 1160.36 741.696 1145.02 728.984C1129.67 716.258 1122 699.269 1122 678.121V677.121H1163.99V678.121C1163.99 688.869 1167.87 697.367 1175.61 703.722L1176.34 704.284C1184.04 709.997 1194.37 712.902 1207.43 712.902C1222.13 712.902 1233.3 710.087 1241.07 704.588C1248.8 698.812 1252.64 691.21 1252.64 681.699C1252.64 674.769 1250.5 669.057 1246.25 664.49L1246.23 664.478L1246.22 664.464C1242.28 659.929 1234.83 656.119 1223.64 653.152L1185.43 644.208L1185.42 644.204C1166.05 639.407 1151.49 632.035 1141.83 622.012L1141.83 622.006L1141.82 622C1132.43 611.94 1127.78 598.707 1127.78 582.405C1127.78 568.81 1131.23 556.976 1138.17 546.949L1138.18 546.941L1138.19 546.933C1145.41 536.936 1155.18 529.225 1167.48 523.793L1167.48 523.79C1180.07 518.36 1194.43 515.657 1210.54 515.657ZM1323.39 521.979C1331.68 525.008 1337.55 526.482 1343.51 526.482C1349.48 526.482 1355.64 525.005 1364.49 521.973L1365.82 521.52V742.633H1322.05V521.489L1323.39 521.979ZM1642.01 515.657C1667.11 515.657 1686.94 523.031 1701.39 537.876C1715.83 552.716 1723 572.968 1723 598.507V742.633H1680.12V608.794C1680.12 591.666 1675.72 578.681 1667.07 569.681L1667.06 569.669L1667.04 569.656C1658.67 560.359 1647.26 555.675 1632.68 555.675C1622.47 555.675 1613.47 558.022 1605.64 562.69L1605.63 562.696C1598.11 567.064 1592.17 573.475 1587.8 581.968C1583.44 590.448 1581.25 600.424 1581.25 611.925V742.633H1537.92V608.347C1537.92 591.208 1533.67 578.376 1525.31 569.68L1525.31 569.674L1525.3 569.668C1516.93 560.664 1505.52 556.122 1490.93 556.122C1480.72 556.122 1471.72 558.469 1463.89 563.138L1463.88 563.144C1456.36 567.511 1450.41 573.922 1446.05 582.415L1446.05 582.422L1446.04 582.428C1441.69 590.602 1439.5 600.423 1439.5 611.925V742.633H1395.72V521.919H1435.05V554.803C1439.92 544.379 1447.91 535.465 1458.37 528.356C1470.71 519.875 1485.58 515.657 1502.93 515.657C1522.37 515.657 1538.61 520.931 1551.55 531.538C1560.38 538.771 1567.1 547.628 1571.72 558.091C1576.05 547.619 1582.83 538.757 1592.07 531.524C1605.61 520.93 1622.28 515.657 1642.01 515.657ZM1343.49 452C1351.45 452 1358.23 454.786 1363.75 460.346C1369.27 465.905 1372.04 472.721 1372.04 480.73C1372.04 488.452 1369.27 495.254 1363.77 501.096L1363.76 501.105L1363.75 501.115C1358.23 506.675 1351.45 509.461 1343.49 509.461C1335.81 509.461 1329.05 506.669 1323.25 501.134L1323.23 501.115L1323.21 501.096C1317.71 495.254 1314.94 488.452 1314.94 480.73C1314.94 472.721 1317.7 465.905 1323.23 460.346L1323.24 460.337L1323.25 460.327C1329.05 454.792 1335.81 452 1343.49 452Z'
fill='#fafafa'
/>
</svg>
)
}
/**
* Generates dynamic Open Graph images for documentation pages.
* Style matches Cursor docs: dark background, title at top, logo bottom-left, domain bottom-right.
*/
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const title = searchParams.get('title') || 'Documentation'
const category = searchParams.get('category') || 'DOCUMENTATION'
const description = searchParams.get('description') || ''
const baseUrl = new URL(request.url).origin
const allText = `${title}${category}${description}docs.sim.ai`
const allText = `${title}docs.sim.ai`
const fontData = await loadGoogleFont('Geist', '400;500;600', allText)
return new ImageResponse(
@@ -54,116 +78,40 @@ export async function GET(request: NextRequest) {
width: '100%',
display: 'flex',
flexDirection: 'column',
background: '#0c0c0c',
position: 'relative',
justifyContent: 'space-between',
padding: '56px 64px',
background: '#121212', // Dark mode background matching docs (hsla 0, 0%, 7%)
fontFamily: 'Geist',
}}
>
{/* Base gradient layer - very subtle purple tint across the entire image */}
<div
{/* Title at top */}
<span
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
background:
'radial-gradient(ellipse 150% 100% at 50% 100%, rgba(88, 28, 135, 0.15) 0%, rgba(88, 28, 135, 0.08) 25%, rgba(88, 28, 135, 0.03) 50%, transparent 80%)',
display: 'flex',
}}
/>
{/* Secondary glow - adds depth without harsh edges */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
background:
'radial-gradient(ellipse 100% 80% at 80% 90%, rgba(112, 31, 252, 0.12) 0%, rgba(112, 31, 252, 0.04) 40%, transparent 70%)',
display: 'flex',
}}
/>
{/* Top darkening - creates natural vignette */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
background:
'linear-gradient(180deg, rgba(0, 0, 0, 0.3) 0%, transparent 40%, transparent 100%)',
display: 'flex',
}}
/>
{/* Content */}
<div
style={{
display: 'flex',
flexDirection: 'column',
padding: '56px 72px',
height: '100%',
justifyContent: 'space-between',
fontSize: getTitleFontSize(title),
fontWeight: 500,
color: '#fafafa', // Light text matching docs
lineHeight: 1.2,
letterSpacing: '-0.02em',
}}
>
{/* Logo */}
<img src={`${baseUrl}/static/logo.png`} alt='sim' height={32} />
{title}
</span>
{/* Category + Title + Description */}
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: 12,
}}
>
<span
style={{
fontSize: 15,
fontWeight: 600,
color: '#802fff',
letterSpacing: '0.02em',
}}
>
{category}
</span>
<span
style={{
fontSize: getTitleFontSize(title),
fontWeight: 600,
color: '#ffffff',
lineHeight: 1.1,
letterSpacing: '-0.02em',
}}
>
{title}
</span>
{description && (
<span
style={{
fontSize: 18,
fontWeight: 400,
color: '#a1a1aa',
lineHeight: 1.4,
marginTop: 4,
}}
>
{description.length > 100 ? `${description.slice(0, 100)}...` : description}
</span>
)}
</div>
{/* Footer */}
{/* Footer: icon left, domain right */}
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
}}
>
<SimLogoFull />
<span
style={{
fontSize: 15,
fontWeight: 500,
color: '#52525b',
fontSize: 20,
fontWeight: 400,
color: '#71717a',
}}
>
docs.sim.ai

View File

@@ -1,16 +1,211 @@
import { createFromSource } from 'fumadocs-core/search/server'
import { source } from '@/lib/source'
import { sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { db, docsEmbeddings } from '@/lib/db'
import { generateSearchEmbedding } from '@/lib/embeddings'
export const revalidate = 3600 // Revalidate every hour
export const runtime = 'nodejs'
export const revalidate = 0
export const { GET } = createFromSource(source, {
localeMap: {
en: { language: 'english' },
es: { language: 'spanish' },
fr: { language: 'french' },
de: { language: 'german' },
// ja and zh are not supported by the stemmer library, so we'll skip language config for them
ja: {},
zh: {},
},
})
/**
* Hybrid search API endpoint
* - English: Vector embeddings + keyword search
* - Other languages: Keyword search only
*/
export async function GET(request: NextRequest) {
try {
const searchParams = request.nextUrl.searchParams
const query = searchParams.get('query') || searchParams.get('q') || ''
const locale = searchParams.get('locale') || 'en'
const limit = Number.parseInt(searchParams.get('limit') || '10', 10)
if (!query || query.trim().length === 0) {
return NextResponse.json([])
}
const candidateLimit = limit * 3
const similarityThreshold = 0.6
const localeMap: Record<string, string> = {
en: 'english',
es: 'spanish',
fr: 'french',
de: 'german',
ja: 'simple', // PostgreSQL doesn't have Japanese support, use simple
zh: 'simple', // PostgreSQL doesn't have Chinese support, use simple
}
const tsConfig = localeMap[locale] || 'simple'
const useVectorSearch = locale === 'en'
let vectorResults: Array<{
chunkId: string
chunkText: string
sourceDocument: string
sourceLink: string
headerText: string
headerLevel: number
similarity: number
searchType: string
}> = []
if (useVectorSearch) {
const queryEmbedding = await generateSearchEmbedding(query)
vectorResults = await db
.select({
chunkId: docsEmbeddings.chunkId,
chunkText: docsEmbeddings.chunkText,
sourceDocument: docsEmbeddings.sourceDocument,
sourceLink: docsEmbeddings.sourceLink,
headerText: docsEmbeddings.headerText,
headerLevel: docsEmbeddings.headerLevel,
similarity: sql<number>`1 - (${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector)`,
searchType: sql<string>`'vector'`,
})
.from(docsEmbeddings)
.where(
sql`1 - (${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector) >= ${similarityThreshold}`
)
.orderBy(sql`${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector`)
.limit(candidateLimit)
}
const keywordResults = await db
.select({
chunkId: docsEmbeddings.chunkId,
chunkText: docsEmbeddings.chunkText,
sourceDocument: docsEmbeddings.sourceDocument,
sourceLink: docsEmbeddings.sourceLink,
headerText: docsEmbeddings.headerText,
headerLevel: docsEmbeddings.headerLevel,
similarity: sql<number>`ts_rank(${docsEmbeddings.chunkTextTsv}, plainto_tsquery(${tsConfig}, ${query}))`,
searchType: sql<string>`'keyword'`,
})
.from(docsEmbeddings)
.where(sql`${docsEmbeddings.chunkTextTsv} @@ plainto_tsquery(${tsConfig}, ${query})`)
.orderBy(
sql`ts_rank(${docsEmbeddings.chunkTextTsv}, plainto_tsquery(${tsConfig}, ${query})) DESC`
)
.limit(candidateLimit)
const knownLocales = ['en', 'es', 'fr', 'de', 'ja', 'zh']
const vectorRankMap = new Map<string, number>()
vectorResults.forEach((r, idx) => vectorRankMap.set(r.chunkId, idx + 1))
const keywordRankMap = new Map<string, number>()
keywordResults.forEach((r, idx) => keywordRankMap.set(r.chunkId, idx + 1))
const allChunkIds = new Set([
...vectorResults.map((r) => r.chunkId),
...keywordResults.map((r) => r.chunkId),
])
const k = 60
type ResultWithRRF = (typeof vectorResults)[0] & { rrfScore: number }
const scoredResults: ResultWithRRF[] = []
for (const chunkId of allChunkIds) {
const vectorRank = vectorRankMap.get(chunkId) ?? Number.POSITIVE_INFINITY
const keywordRank = keywordRankMap.get(chunkId) ?? Number.POSITIVE_INFINITY
const rrfScore = 1 / (k + vectorRank) + 1 / (k + keywordRank)
const result =
vectorResults.find((r) => r.chunkId === chunkId) ||
keywordResults.find((r) => r.chunkId === chunkId)
if (result) {
scoredResults.push({ ...result, rrfScore })
}
}
scoredResults.sort((a, b) => b.rrfScore - a.rrfScore)
const localeFilteredResults = scoredResults.filter((result) => {
const firstPart = result.sourceDocument.split('/')[0]
if (knownLocales.includes(firstPart)) {
return firstPart === locale
}
return locale === 'en'
})
const queryLower = query.toLowerCase()
const getTitleBoost = (result: ResultWithRRF): number => {
const fileName = result.sourceDocument
.replace('.mdx', '')
.split('/')
.pop()
?.toLowerCase()
?.replace(/_/g, ' ')
if (fileName === queryLower) return 0.01
if (fileName?.includes(queryLower)) return 0.005
return 0
}
localeFilteredResults.sort((a, b) => {
return b.rrfScore + getTitleBoost(b) - (a.rrfScore + getTitleBoost(a))
})
const pageMap = new Map<string, ResultWithRRF>()
for (const result of localeFilteredResults) {
const pageKey = result.sourceDocument
const existing = pageMap.get(pageKey)
if (!existing || result.rrfScore > existing.rrfScore) {
pageMap.set(pageKey, result)
}
}
const deduplicatedResults = Array.from(pageMap.values())
.sort((a, b) => b.rrfScore + getTitleBoost(b) - (a.rrfScore + getTitleBoost(a)))
.slice(0, limit)
const searchResults = deduplicatedResults.map((result) => {
const title = result.headerText || result.sourceDocument.replace('.mdx', '')
const pathParts = result.sourceDocument
.replace('.mdx', '')
.split('/')
.filter((part) => part !== 'index' && !knownLocales.includes(part))
.map((part) => {
return part
.replace(/_/g, ' ')
.split(' ')
.map((word) => {
const acronyms = [
'api',
'mcp',
'sdk',
'url',
'http',
'json',
'xml',
'html',
'css',
'ai',
]
if (acronyms.includes(word.toLowerCase())) {
return word.toUpperCase()
}
return word.charAt(0).toUpperCase() + word.slice(1)
})
.join(' ')
})
return {
id: result.chunkId,
type: 'page' as const,
url: result.sourceLink,
content: title,
breadcrumbs: pathParts,
}
})
return NextResponse.json(searchResults)
} catch (error) {
console.error('Semantic search error:', error)
return NextResponse.json([])
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared'
/**
* Shared layout configurations
*
* you can customise layouts individually from:
* Home Layout: app/(home)/layout.tsx
* Docs Layout: app/docs/layout.tsx
*/
export const baseOptions: BaseLayoutProps = {
nav: {
title: (
<>
<svg width='24' height='24' xmlns='http://www.w3.org/2000/svg' aria-label='Logo'>
<circle cx={12} cy={12} r={12} fill='currentColor' />
</svg>
My App
</>
),
},
}

View File

@@ -7,26 +7,27 @@ export default function RootLayout({ children }: { children: ReactNode }) {
export const metadata = {
metadataBase: new URL('https://docs.sim.ai'),
title: {
default: 'Sim Documentation - Visual Workflow Builder for AI Applications',
default: 'Sim Documentation — Build AI Agents & Run Your Agentic Workforce',
template: '%s',
},
description:
'Comprehensive documentation for Sim - the visual workflow builder for AI applications. Create powerful AI agents, automation workflows, and data processing pipelines by connecting blocks on a canvas—no coding required.',
'Documentation for Sim the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows.',
keywords: [
'AI workflow builder',
'visual workflow editor',
'AI automation',
'workflow automation',
'AI agents',
'no-code AI',
'drag and drop workflows',
'agentic workforce',
'AI agent platform',
'open-source AI agents',
'agentic workflows',
'LLM orchestration',
'AI integrations',
'workflow canvas',
'AI Agent Workflow Builder',
'workflow orchestration',
'agent builder',
'AI workflow automation',
'visual programming',
'knowledge base',
'AI automation',
'workflow builder',
'AI workflow orchestration',
'enterprise AI',
'AI agent deployment',
'intelligent automation',
'AI tools',
],
authors: [{ name: 'Sim Team', url: 'https://sim.ai' }],
creator: 'Sim',
@@ -53,12 +54,12 @@ export const metadata = {
alternateLocale: ['es_ES', 'fr_FR', 'de_DE', 'ja_JP', 'zh_CN'],
url: 'https://docs.sim.ai',
siteName: 'Sim Documentation',
title: 'Sim Documentation - Visual Workflow Builder for AI Applications',
title: 'Sim Documentation — Build AI Agents & Run Your Agentic Workforce',
description:
'Comprehensive documentation for Sim - the visual workflow builder for AI applications. Create powerful AI agents, automation workflows, and data processing pipelines.',
'Documentation for Sim the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows.',
images: [
{
url: 'https://docs.sim.ai/api/og?title=Sim%20Documentation&category=DOCUMENTATION',
url: 'https://docs.sim.ai/api/og?title=Sim%20Documentation',
width: 1200,
height: 630,
alt: 'Sim Documentation',
@@ -67,12 +68,12 @@ export const metadata = {
},
twitter: {
card: 'summary_large_image',
title: 'Sim Documentation - Visual Workflow Builder for AI Applications',
title: 'Sim Documentation — Build AI Agents & Run Your Agentic Workforce',
description:
'Comprehensive documentation for Sim - the visual workflow builder for AI applications.',
'Documentation for Sim the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows.',
creator: '@simdotai',
site: '@simdotai',
images: ['https://docs.sim.ai/api/og?title=Sim%20Documentation&category=DOCUMENTATION'],
images: ['https://docs.sim.ai/api/og?title=Sim%20Documentation'],
},
robots: {
index: true,

View File

@@ -6,7 +6,10 @@ import { source } from '@/lib/source'
export const revalidate = false
export async function GET(_req: NextRequest, { params }: { params: Promise<{ slug?: string[] }> }) {
export async function GET(
_request: NextRequest,
{ params }: { params: Promise<{ slug?: string[] }> }
) {
const { slug } = await params
let lang: (typeof i18n.languages)[number] = i18n.defaultLanguage

View File

@@ -37,9 +37,9 @@ export async function GET() {
const manifest = `# Sim Documentation
> Visual Workflow Builder for AI Applications
> The open-source platform to build AI agents and run your agentic workforce.
Sim is a visual workflow builder for AI applications that lets you build AI agent workflows visually. Create powerful AI agents, automation workflows, and data processing pipelines by connecting blocks on a canvas—no coding required.
Sim is the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows. Create agents, workflows, knowledge bases, tables, and docs. Trusted by over 100,000 builders.
## Documentation Overview

View File

@@ -44,7 +44,7 @@ export function SidebarItem({ item }: { item: Item }) {
'lg:text-gray-600 lg:dark:text-gray-400',
!active && 'lg:hover:bg-gray-100/60 lg:dark:hover:bg-gray-800/40',
active &&
'lg:bg-purple-50/80 lg:font-normal lg:text-purple-600 lg:dark:bg-purple-900/15 lg:dark:text-purple-400'
'lg:bg-emerald-50/80 lg:font-normal lg:text-emerald-600 lg:dark:bg-emerald-900/15 lg:dark:text-emerald-400'
)}
>
{item.name}
@@ -52,15 +52,26 @@ export function SidebarItem({ item }: { item: Item }) {
)
}
function isApiReferenceFolder(node: Folder): boolean {
if (node.index?.url.includes('/api-reference/')) return true
for (const child of node.children) {
if (child.type === 'page' && child.url.includes('/api-reference/')) return true
if (child.type === 'folder' && isApiReferenceFolder(child)) return true
}
return false
}
export function SidebarFolder({ item, children }: { item: Folder; children: ReactNode }) {
const pathname = usePathname()
const hasActiveChild = checkHasActiveChild(item, pathname)
const isApiRef = isApiReferenceFolder(item)
const isOnApiRefPage = stripLangPrefix(pathname).startsWith('/api-reference')
const hasChildren = item.children.length > 0
const [open, setOpen] = useState(hasActiveChild)
const [open, setOpen] = useState(hasActiveChild || (isApiRef && isOnApiRefPage))
useEffect(() => {
setOpen(hasActiveChild)
}, [hasActiveChild])
setOpen(hasActiveChild || (isApiRef && isOnApiRefPage))
}, [hasActiveChild, isApiRef, isOnApiRefPage])
const active = item.index ? isActive(item.index.url, pathname, false) : false
@@ -79,7 +90,7 @@ export function SidebarFolder({ item, children }: { item: Folder; children: Reac
'lg:text-gray-600 lg:dark:text-gray-400',
!active && 'lg:hover:bg-gray-100/60 lg:dark:hover:bg-gray-800/40',
active &&
'lg:bg-purple-50/80 lg:font-normal lg:text-purple-600 lg:dark:bg-purple-900/15 lg:dark:text-purple-400'
'lg:bg-emerald-50/80 lg:font-normal lg:text-emerald-600 lg:dark:bg-emerald-900/15 lg:dark:text-emerald-400'
)}
>
{item.name}
@@ -104,7 +115,7 @@ export function SidebarFolder({ item, children }: { item: Folder; children: Reac
'lg:text-gray-800 lg:dark:text-gray-200',
!active && 'lg:hover:bg-gray-100/60 lg:dark:hover:bg-gray-800/40',
active &&
'lg:bg-purple-50/80 lg:text-purple-600 lg:dark:bg-purple-900/15 lg:dark:text-purple-400'
'lg:bg-emerald-50/80 lg:text-emerald-600 lg:dark:bg-emerald-900/15 lg:dark:text-emerald-400'
)}
>
{item.name}
@@ -157,16 +168,18 @@ export function SidebarFolder({ item, children }: { item: Folder; children: Reac
{hasChildren && (
<div
className={cn(
'overflow-hidden transition-all duration-200 ease-in-out',
open ? 'max-h-[10000px] opacity-100' : 'max-h-0 opacity-0'
'grid transition-[grid-template-rows,opacity] duration-200 ease-in-out',
open ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'
)}
>
{/* Mobile: simple indent */}
<div className='ml-4 flex flex-col gap-0.5 lg:hidden'>{children}</div>
{/* Desktop: styled with border */}
<ul className='mt-0.5 ml-2 hidden space-y-[0.0625rem] border-gray-200/60 border-l pl-2.5 lg:block dark:border-gray-700/60'>
{children}
</ul>
<div className='overflow-hidden'>
{/* Mobile: simple indent */}
<div className='ml-4 flex flex-col gap-0.5 lg:hidden'>{children}</div>
{/* Desktop: styled with border */}
<ul className='mt-0.5 ml-2 hidden space-y-[0.0625rem] border-gray-200/60 border-l pl-2.5 lg:block dark:border-gray-700/60'>
{children}
</ul>
</div>
</div>
)}
</div>

View File

@@ -1,38 +1,36 @@
'use client'
import { useState } from 'react'
import { ArrowRight, ChevronRight } from 'lucide-react'
import Link from 'next/link'
export function TOCFooter() {
const [isHovered, setIsHovered] = useState(false)
return (
<div className='sticky bottom-0 mt-6'>
<div className='flex flex-col gap-2 rounded-lg border border-border bg-secondary p-6 text-sm'>
<div className='text-balance font-semibold text-base leading-tight'>
Start building today
</div>
<div className='text-muted-foreground'>Trusted by over 60,000 builders.</div>
<div className='text-muted-foreground'>Trusted by over 100,000 builders.</div>
<div className='text-muted-foreground'>
Build Agentic workflows visually on a drag-and-drop canvas or with natural language.
The open-source platform to build AI agents and run your agentic workforce.
</div>
<Link
href='https://sim.ai/signup'
target='_blank'
rel='noopener noreferrer'
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className='group mt-2 inline-flex h-8 w-fit items-center justify-center gap-1 whitespace-nowrap rounded-[10px] border border-[#6F3DFA] bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] px-3 pr-[10px] pl-[12px] font-medium text-sm text-white shadow-[inset_0_2px_4px_0_#9B77FF] outline-none transition-all hover:shadow-lg focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50'
className='group mt-2 inline-flex h-8 w-fit items-center justify-center gap-2 whitespace-nowrap rounded-[5px] border border-[#33C482] bg-[#33C482] px-[10px] font-medium text-black text-sm outline-none transition-[filter] hover:brightness-110 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50'
aria-label='Get started with Sim - Sign up for free'
>
<span>Get started</span>
<span className='inline-flex transition-transform duration-200 group-hover:translate-x-0.5'>
{isHovered ? (
<ArrowRight className='h-4 w-4' aria-hidden='true' />
) : (
<ChevronRight className='h-4 w-4' aria-hidden='true' />
)}
<span className='relative inline-flex h-4 w-4 transition-transform duration-200 group-hover:translate-x-0.5'>
<ChevronRight
className='absolute inset-0 h-4 w-4 transition-opacity duration-200 group-hover:opacity-0'
aria-hidden='true'
/>
<ArrowRight
className='absolute inset-0 h-4 w-4 opacity-0 transition-opacity duration-200 group-hover:opacity-100'
aria-hidden='true'
/>
</span>
</Link>
</div>

File diff suppressed because one or more lines are too long

View File

@@ -1,65 +1,95 @@
'use client'
import Image from 'next/image'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { LanguageDropdown } from '@/components/ui/language-dropdown'
import { SearchTrigger } from '@/components/ui/search-trigger'
import { SimLogoFull } from '@/components/ui/sim-logo'
import { ThemeToggle } from '@/components/ui/theme-toggle'
import { cn } from '@/lib/utils'
const NAV_TABS = [
{
label: 'Documentation',
href: '/introduction',
match: (p: string) => !p.includes('/api-reference'),
external: false,
},
{
label: 'API Reference',
href: '/api-reference/getting-started',
match: (p: string) => p.includes('/api-reference'),
external: false,
},
{ label: 'Mothership', href: 'https://sim.ai', external: true },
] as const
export function Navbar() {
const pathname = usePathname()
return (
<nav
className='sticky top-0 z-50 border-border/50 border-b'
style={{
backdropFilter: 'blur(25px) saturate(180%)',
WebkitBackdropFilter: 'blur(25px) saturate(180%)',
}}
>
{/* Desktop: Single row layout */}
<div className='hidden h-16 w-full items-center lg:flex'>
<nav className='sticky top-0 z-50 bg-background/80 backdrop-blur-md backdrop-saturate-150'>
<div className='hidden w-full flex-col lg:flex'>
{/* Top row: logo, search, controls */}
<div
className='relative flex w-full items-center justify-between'
className='relative flex h-[52px] w-full items-center justify-between'
style={{
paddingLeft: 'calc(var(--sidebar-offset) + 32px)',
paddingRight: 'calc(var(--toc-offset) + 60px)',
}}
>
{/* Left cluster: logo */}
<div className='flex items-center'>
<Link href='/' className='flex min-w-[100px] items-center'>
<Image
src='/static/logo.png'
alt='Sim'
width={72}
height={28}
className='h-7 w-auto'
/>
</Link>
</div>
<Link href='/' className='flex min-w-[100px] items-center'>
<SimLogoFull className='h-7 w-auto' />
</Link>
{/* Center cluster: search - absolutely positioned to center */}
<div className='-translate-x-1/2 absolute left-1/2 flex items-center justify-center'>
<SearchTrigger />
</div>
{/* Right cluster aligns with TOC edge */}
<div className='flex items-center gap-4'>
<Link
href='https://sim.ai'
target='_blank'
rel='noopener noreferrer'
className='rounded-xl px-3 py-2 font-normal text-[0.9375rem] text-foreground/60 leading-[1.4] transition-colors hover:bg-foreground/8 hover:text-foreground'
style={{
fontFamily:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
}}
>
Platform
</Link>
<div className='flex items-center gap-1'>
<LanguageDropdown />
<ThemeToggle />
</div>
</div>
{/* Divider — only spans content width */}
<div
className='border-b'
style={{
marginLeft: 'calc(var(--sidebar-offset) + 32px)',
marginRight: 'calc(var(--toc-offset) + 60px)',
borderColor: 'rgba(128, 128, 128, 0.1)',
}}
/>
{/* Bottom row: navigation tabs — border on row, tabs overlap it */}
<div
className='flex h-[40px] items-stretch gap-6 border-border/20 border-b'
style={{
paddingLeft: 'calc(var(--sidebar-offset) + 32px)',
}}
>
{NAV_TABS.map((tab) => {
const isActive = !tab.external && tab.match(pathname)
return (
<Link
key={tab.label}
href={tab.href}
{...(tab.external ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
className={cn(
'-mb-px relative flex items-center border-b text-[14px] tracking-[-0.01em] transition-colors',
isActive
? 'border-neutral-400 font-[550] text-neutral-800 dark:border-neutral-500 dark:text-neutral-200'
: 'border-transparent font-medium text-fd-muted-foreground hover:border-neutral-300 hover:text-neutral-600 dark:hover:border-neutral-600 dark:hover:text-neutral-400'
)}
>
{/* Invisible bold text reserves width to prevent layout shift */}
<span className='invisible font-[550]'>{tab.label}</span>
<span className='absolute'>{tab.label}</span>
</Link>
)
})}
</div>
</div>
</nav>
)

View File

@@ -1,45 +1,13 @@
'use client'
import { useState } from 'react'
import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button'
import { Check, Copy } from 'lucide-react'
const cache = new Map<string, string>()
export function LLMCopyButton({
markdownUrl,
}: {
/**
* A URL to fetch the raw Markdown/MDX content of page
*/
markdownUrl: string
}) {
const [isLoading, setLoading] = useState(false)
const [checked, onClick] = useCopyButton(async () => {
const cached = cache.get(markdownUrl)
if (cached) return navigator.clipboard.writeText(cached)
setLoading(true)
try {
await navigator.clipboard.write([
new ClipboardItem({
'text/plain': fetch(markdownUrl).then(async (res) => {
const content = await res.text()
cache.set(markdownUrl, content)
return content
}),
}),
])
} finally {
setLoading(false)
}
})
export function LLMCopyButton({ content }: { content: string }) {
const [checked, onClick] = useCopyButton(() => navigator.clipboard.writeText(content))
return (
<button
disabled={isLoading}
onClick={onClick}
className='flex cursor-pointer items-center gap-1.5 rounded-lg border border-border/40 bg-background px-2.5 py-2 text-muted-foreground/60 text-sm leading-none transition-all hover:border-border hover:bg-accent/50 hover:text-muted-foreground'
aria-label={checked ? 'Copied to clipboard' : 'Copy page content'}

View File

@@ -25,8 +25,8 @@ export function StructuredData({
headline: title,
description: description,
url: url,
datePublished: dateModified || new Date().toISOString(),
dateModified: dateModified || new Date().toISOString(),
...(dateModified && { datePublished: dateModified }),
...(dateModified && { dateModified }),
author: {
'@type': 'Organization',
name: 'Sim Team',
@@ -74,7 +74,7 @@ export function StructuredData({
name: 'Sim Documentation',
url: baseUrl,
description:
'Comprehensive documentation for Sim visual workflow builder for AI applications. Create powerful AI agents, automation workflows, and data processing pipelines.',
'Documentation for Sim — the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows.',
publisher: {
'@type': 'Organization',
name: 'Sim',
@@ -91,12 +91,6 @@ export function StructuredData({
inLanguage: ['en', 'es', 'fr', 'de', 'ja', 'zh'],
}
const faqStructuredData = title.toLowerCase().includes('faq') && {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: [],
}
const softwareStructuredData = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
@@ -104,7 +98,7 @@ export function StructuredData({
applicationCategory: 'DeveloperApplication',
operatingSystem: 'Any',
description:
'Visual workflow builder for AI applications. Create powerful AI agents, automation workflows, and data processing pipelines by connecting blocks on a canvas—no coding required.',
'Sim is the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows. Create agents, workflows, knowledge bases, tables, and docs.',
url: baseUrl,
author: {
'@type': 'Organization',
@@ -115,12 +109,13 @@ export function StructuredData({
category: 'Developer Tools',
},
featureList: [
'Visual workflow builder with drag-and-drop interface',
'AI agent creation and automation',
'80+ built-in integrations',
'Real-time team collaboration',
'Multiple deployment options',
'Custom integrations via MCP protocol',
'AI agent creation',
'Agentic workflow orchestration',
'1,000+ integrations',
'LLM orchestration (OpenAI, Anthropic, Google, xAI, Mistral, Perplexity)',
'Knowledge base creation',
'Table creation',
'Document creation',
],
}
@@ -151,15 +146,6 @@ export function StructuredData({
}}
/>
)}
{faqStructuredData && (
<Script
id='faq-structured-data'
type='application/ld+json'
dangerouslySetInnerHTML={{
__html: JSON.stringify(faqStructuredData),
}}
/>
)}
{url === baseUrl && (
<Script
id='software-structured-data'

View File

@@ -0,0 +1,87 @@
'use client'
import { useState } from 'react'
import { cn, getAssetUrl } from '@/lib/utils'
import { Lightbox } from './lightbox'
interface ActionImageProps {
src: string
alt: string
enableLightbox?: boolean
}
interface ActionVideoProps {
src: string
alt: string
enableLightbox?: boolean
}
export function ActionImage({ src, alt, enableLightbox = true }: ActionImageProps) {
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
const handleClick = () => {
if (enableLightbox) {
setIsLightboxOpen(true)
}
}
return (
<>
<img
src={src}
alt={alt}
onClick={handleClick}
className={cn(
'inline-block w-full max-w-[200px] rounded border border-neutral-200 dark:border-neutral-700',
enableLightbox && 'cursor-pointer transition-opacity hover:opacity-90'
)}
/>
{enableLightbox && (
<Lightbox
isOpen={isLightboxOpen}
onClose={() => setIsLightboxOpen(false)}
src={src}
alt={alt}
type='image'
/>
)}
</>
)
}
export function ActionVideo({ src, alt, enableLightbox = true }: ActionVideoProps) {
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
const resolvedSrc = getAssetUrl(src)
const handleClick = () => {
if (enableLightbox) {
setIsLightboxOpen(true)
}
}
return (
<>
<video
src={resolvedSrc}
autoPlay
loop
muted
playsInline
onClick={handleClick}
className={cn(
'inline-block w-full max-w-[200px] rounded border border-neutral-200 dark:border-neutral-700',
enableLightbox && 'cursor-pointer transition-opacity hover:opacity-90'
)}
/>
{enableLightbox && (
<Lightbox
isOpen={isLightboxOpen}
onClose={() => setIsLightboxOpen(false)}
src={src}
alt={alt}
type='video'
/>
)}
</>
)
}

View File

@@ -0,0 +1,195 @@
import { memo } from 'react'
const RX = '2.59574'
interface BlockRect {
opacity: number
width: string
height: string
fill: string
x?: string
y?: string
transform?: string
}
const RECTS = {
topRight: [
{ opacity: 1, x: '0', y: '0', width: '16.8626', height: '33.7252', fill: '#2ABBF8' },
{ opacity: 0.6, x: '0', y: '0', width: '85.3433', height: '16.8626', fill: '#2ABBF8' },
{ opacity: 1, x: '0', y: '0', width: '16.8626', height: '16.8626', fill: '#2ABBF8' },
{ opacity: 0.6, x: '34.2403', y: '0', width: '34.2403', height: '33.7252', fill: '#2ABBF8' },
{ opacity: 1, x: '34.2403', y: '0', width: '16.8626', height: '16.8626', fill: '#2ABBF8' },
{
opacity: 1,
x: '51.6188',
y: '16.8626',
width: '16.8626',
height: '16.8626',
fill: '#2ABBF8',
},
{ opacity: 1, x: '68.4812', y: '0', width: '54.6502', height: '16.8626', fill: '#00F701' },
{ opacity: 0.6, x: '106.268', y: '0', width: '34.2403', height: '33.7252', fill: '#00F701' },
{ opacity: 0.6, x: '106.268', y: '0', width: '51.103', height: '16.8626', fill: '#00F701' },
{
opacity: 1,
x: '123.6484',
y: '16.8626',
width: '16.8626',
height: '16.8626',
fill: '#00F701',
},
{ opacity: 0.6, x: '157.371', y: '0', width: '34.2403', height: '16.8626', fill: '#FFCC02' },
{ opacity: 1, x: '157.371', y: '0', width: '16.8626', height: '16.8626', fill: '#FFCC02' },
{ opacity: 0.6, x: '208.993', y: '0', width: '68.4805', height: '16.8626', fill: '#FA4EDF' },
{ opacity: 0.6, x: '209.137', y: '0', width: '16.8626', height: '33.7252', fill: '#FA4EDF' },
{ opacity: 0.6, x: '243.233', y: '0', width: '34.2403', height: '33.7252', fill: '#FA4EDF' },
{ opacity: 1, x: '243.233', y: '0', width: '16.8626', height: '16.8626', fill: '#FA4EDF' },
{ opacity: 0.6, x: '260.096', y: '0', width: '34.04', height: '16.8626', fill: '#FA4EDF' },
{
opacity: 1,
x: '260.611',
y: '16.8626',
width: '16.8626',
height: '16.8626',
fill: '#FA4EDF',
},
],
bottomLeft: [
{ opacity: 1, x: '0', y: '0', width: '16.8626', height: '33.7252', fill: '#2ABBF8' },
{ opacity: 0.6, x: '0', y: '0', width: '85.3433', height: '16.8626', fill: '#2ABBF8' },
{ opacity: 1, x: '0', y: '0', width: '16.8626', height: '16.8626', fill: '#2ABBF8' },
{ opacity: 0.6, x: '34.2403', y: '0', width: '34.2403', height: '33.7252', fill: '#2ABBF8' },
{ opacity: 1, x: '34.2403', y: '0', width: '16.8626', height: '16.8626', fill: '#2ABBF8' },
{
opacity: 1,
x: '51.6188',
y: '16.8626',
width: '16.8626',
height: '16.8626',
fill: '#2ABBF8',
},
{ opacity: 1, x: '68.4812', y: '0', width: '54.6502', height: '16.8626', fill: '#00F701' },
{ opacity: 0.6, x: '106.268', y: '0', width: '34.2403', height: '33.7252', fill: '#00F701' },
{ opacity: 0.6, x: '106.268', y: '0', width: '51.103', height: '16.8626', fill: '#00F701' },
{
opacity: 1,
x: '123.6484',
y: '16.8626',
width: '16.8626',
height: '16.8626',
fill: '#00F701',
},
],
bottomRight: [
{
opacity: 0.6,
width: '16.8626',
height: '33.726',
fill: '#FA4EDF',
transform: 'matrix(0 1 1 0 0 0)',
},
{
opacity: 0.6,
width: '34.241',
height: '16.8626',
fill: '#FA4EDF',
transform: 'matrix(0 1 1 0 16.891 0)',
},
{
opacity: 0.6,
width: '16.8626',
height: '68.482',
fill: '#FA4EDF',
transform: 'matrix(-1 0 0 1 33.739 16.888)',
},
{
opacity: 0.6,
width: '16.8626',
height: '33.726',
fill: '#FA4EDF',
transform: 'matrix(0 1 1 0 0 33.776)',
},
{
opacity: 1,
width: '16.8626',
height: '16.8626',
fill: '#FA4EDF',
transform: 'matrix(-1 0 0 1 33.739 34.272)',
},
{
opacity: 0.6,
width: '16.8626',
height: '34.24',
fill: '#2ABBF8',
transform: 'matrix(-1 0 0 1 33.787 68)',
},
{
opacity: 0.4,
width: '16.8626',
height: '16.8626',
fill: '#1A8FCC',
transform: 'matrix(-1 0 0 1 33.787 85)',
},
],
} as const satisfies Record<string, readonly BlockRect[]>
const GLOBAL_OPACITY = 0.55
const BlockGroup = memo(function BlockGroup({
width,
height,
viewBox,
rects,
}: {
width: number
height: number
viewBox: string
rects: readonly BlockRect[]
}) {
return (
<svg
width={width}
height={height}
viewBox={viewBox}
fill='none'
xmlns='http://www.w3.org/2000/svg'
className='h-auto w-full'
style={{ opacity: GLOBAL_OPACITY }}
>
{rects.map((r, i) => (
<rect
key={i}
x={r.x}
y={r.y}
width={r.width}
height={r.height}
rx={RX}
fill={r.fill}
transform={r.transform}
opacity={r.opacity}
/>
))}
</svg>
)
})
export function AnimatedBlocks() {
return (
<div
className='pointer-events-none fixed inset-0 z-0 hidden overflow-hidden lg:block'
aria-hidden='true'
>
<div className='absolute top-[93px] right-0 w-[calc(140px+10.76vw)] max-w-[295px]'>
<BlockGroup width={295} height={34} viewBox='0 0 295 34' rects={RECTS.topRight} />
</div>
<div className='-left-24 absolute bottom-0 w-[calc(140px+10.76vw)] max-w-[295px] rotate-180'>
<BlockGroup width={295} height={34} viewBox='0 0 295 34' rects={RECTS.bottomLeft} />
</div>
<div className='-bottom-2 absolute right-0 w-[calc(16px+1.25vw)] max-w-[34px]'>
<BlockGroup width={34} height={102} viewBox='0 0 34 102' rects={RECTS.bottomRight} />
</div>
</div>
)
}

View File

@@ -17,23 +17,16 @@ export function CodeBlock(props: React.ComponentProps<typeof FumadocsCodeBlock>)
return (
<FumadocsCodeBlock
{...props}
Actions={({ children, className }) => (
Actions={({ className }) => (
<div className={cn('empty:hidden', className)}>
{/* Custom copy button */}
<button
type='button'
aria-label={copied ? 'Copied Text' : 'Copy Text'}
onClick={(e) => {
const pre = (e.currentTarget as HTMLElement)
.closest('.nd-codeblock')
?.querySelector('pre')
const pre = (e.currentTarget as HTMLElement).closest('figure')?.querySelector('pre')
if (pre) handleCopy(pre.textContent || '')
}}
className={cn(
'cursor-pointer rounded-md p-2 transition-all',
'border border-border bg-background/80 hover:bg-muted',
'backdrop-blur-sm'
)}
className='cursor-pointer rounded-md p-2 text-muted-foreground transition-colors hover:text-foreground'
>
<span className='flex items-center justify-center'>
{copied ? (

View File

@@ -0,0 +1,73 @@
'use client'
import * as React from 'react'
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
import { Check } from 'lucide-react'
import { cn } from '@/lib/utils'
const DropdownMenu = DropdownMenuPrimitive.Root
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in',
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className
)}
{...props}
/>
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className
)}
checked={checked}
{...props}
>
<span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
<DropdownMenuPrimitive.ItemIndicator>
<Check className='h-4 w-4' />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
))
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
}

View File

@@ -0,0 +1,47 @@
'use client'
import { useState } from 'react'
import { ChevronRight } from 'lucide-react'
interface FAQItem {
question: string
answer: string
}
interface FAQProps {
items: FAQItem[]
title?: string
}
export function FAQ({ items, title = 'Common Questions' }: FAQProps) {
const [openIndex, setOpenIndex] = useState<number | null>(null)
return (
<div className='mt-12'>
<h2 className='mb-4 font-bold text-xl'>{title}</h2>
<div className='rounded-xl border border-border'>
{items.map((item, index) => (
<div key={index} className={index !== items.length - 1 ? 'border-border border-b' : ''}>
<button
type='button'
onClick={() => setOpenIndex(openIndex === index ? null : index)}
className='flex w-full cursor-pointer items-center gap-3 px-5 py-4 text-left font-medium text-[0.9375rem]'
>
<ChevronRight
className={`h-4 w-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 ${
openIndex === index ? 'rotate-90' : ''
}`}
/>
{item.question}
</button>
{openIndex === index && (
<div className='px-5 pb-4 pl-12 text-[0.9375rem] text-fd-muted-foreground leading-relaxed'>
{item.answer}
</div>
)}
</div>
))}
</div>
</div>
)
}

View File

@@ -4,42 +4,76 @@
import type { ComponentType, SVGProps } from 'react'
import {
A2AIcon,
AhrefsIcon,
AirtableIcon,
AirweaveIcon,
AlgoliaIcon,
AmplitudeIcon,
ApifyIcon,
ApolloIcon,
ArxivIcon,
AsanaIcon,
AshbyIcon,
AttioIcon,
BrainIcon,
BrandfetchIcon,
BrowserUseIcon,
CalComIcon,
CalendlyIcon,
CirclebackIcon,
ClayIcon,
ClerkIcon,
CloudflareIcon,
ConfluenceIcon,
CursorIcon,
DatabricksIcon,
DatadogIcon,
DevinIcon,
DiscordIcon,
DocumentIcon,
DropboxIcon,
DsPyIcon,
DubIcon,
DuckDuckGoIcon,
DynamoDBIcon,
ElasticsearchIcon,
ElevenLabsIcon,
EnrichSoIcon,
EvernoteIcon,
ExaAIIcon,
EyeIcon,
FathomIcon,
FirecrawlIcon,
FirefliesIcon,
GammaIcon,
GithubIcon,
GitLabIcon,
GmailIcon,
GongIcon,
GoogleAdsIcon,
GoogleBigQueryIcon,
GoogleBooksIcon,
GoogleCalendarIcon,
GoogleContactsIcon,
GoogleDocsIcon,
GoogleDriveIcon,
GoogleFormsIcon,
GoogleGroupsIcon,
GoogleIcon,
GoogleMapsIcon,
GoogleMeetIcon,
GooglePagespeedIcon,
GoogleSheetsIcon,
GoogleSlidesIcon,
GoogleTasksIcon,
GoogleTranslateIcon,
GoogleVaultIcon,
GrafanaIcon,
GrainIcon,
GreenhouseIcon,
GreptileIcon,
HexIcon,
HubspotIcon,
HuggingFaceIcon,
HunterIOIcon,
@@ -48,13 +82,20 @@ import {
IntercomIcon,
JinaAIIcon,
JiraIcon,
JiraServiceManagementIcon,
KalshiIcon,
LangsmithIcon,
LemlistIcon,
LinearIcon,
LinkedInIcon,
LinkupIcon,
LoopsIcon,
LumaIcon,
MailchimpIcon,
MailgunIcon,
MailServerIcon,
Mem0Icon,
MicrosoftDataverseIcon,
MicrosoftExcelIcon,
MicrosoftOneDriveIcon,
MicrosoftPlannerIcon,
@@ -65,9 +106,12 @@ import {
MySQLIcon,
Neo4jIcon,
NotionIcon,
ObsidianIcon,
OnePasswordIcon,
OpenAIIcon,
OutlookIcon,
PackageSearchIcon,
PagerDutyIcon,
ParallelIcon,
PerplexityIcon,
PineconeIcon,
@@ -75,10 +119,14 @@ import {
PolymarketIcon,
PostgresIcon,
PosthogIcon,
PulseIcon,
QdrantIcon,
RDSIcon,
RedditIcon,
RedisIcon,
ReductoIcon,
ResendIcon,
RevenueCatIcon,
S3Icon,
SalesforceIcon,
SearchIcon,
@@ -88,9 +136,9 @@ import {
ServiceNowIcon,
SftpIcon,
ShopifyIcon,
SimilarwebIcon,
SlackIcon,
SmtpIcon,
SpotifyIcon,
SQSIcon,
SshIcon,
STTIcon,
@@ -99,11 +147,15 @@ import {
SupabaseIcon,
TavilyIcon,
TelegramIcon,
TextractIcon,
TinybirdIcon,
TranslateIcon,
TrelloIcon,
TTSIcon,
TwilioIcon,
TypeformIcon,
UpstashIcon,
VercelIcon,
VideoIcon,
WealthboxIcon,
WebflowIcon,
@@ -120,117 +172,168 @@ import {
type IconComponent = ComponentType<SVGProps<SVGSVGElement>>
export const blockTypeToIconMap: Record<string, IconComponent> = {
zoom: ZoomIcon,
zep: ZepIcon,
zendesk: ZendeskIcon,
youtube: YouTubeIcon,
x: xIcon,
wordpress: WordpressIcon,
wikipedia: WikipediaIcon,
whatsapp: WhatsAppIcon,
webflow: WebflowIcon,
wealthbox: WealthboxIcon,
vision: EyeIcon,
video_generator: VideoIcon,
typeform: TypeformIcon,
twilio_voice: TwilioIcon,
twilio_sms: TwilioIcon,
tts: TTSIcon,
trello: TrelloIcon,
translate: TranslateIcon,
thinking: BrainIcon,
telegram: TelegramIcon,
tavily: TavilyIcon,
supabase: SupabaseIcon,
stt: STTIcon,
stripe: StripeIcon,
stagehand: StagehandIcon,
ssh: SshIcon,
sqs: SQSIcon,
spotify: SpotifyIcon,
smtp: SmtpIcon,
slack: SlackIcon,
shopify: ShopifyIcon,
sharepoint: MicrosoftSharepointIcon,
sftp: SftpIcon,
servicenow: ServiceNowIcon,
serper: SerperIcon,
sentry: SentryIcon,
sendgrid: SendgridIcon,
search: SearchIcon,
salesforce: SalesforceIcon,
s3: S3Icon,
resend: ResendIcon,
reddit: RedditIcon,
rds: RDSIcon,
qdrant: QdrantIcon,
posthog: PosthogIcon,
postgresql: PostgresIcon,
polymarket: PolymarketIcon,
pipedrive: PipedriveIcon,
pinecone: PineconeIcon,
perplexity: PerplexityIcon,
parallel_ai: ParallelIcon,
outlook: OutlookIcon,
openai: OpenAIIcon,
onedrive: MicrosoftOneDriveIcon,
notion: NotionIcon,
neo4j: Neo4jIcon,
mysql: MySQLIcon,
mongodb: MongoDBIcon,
mistral_parse: MistralIcon,
microsoft_teams: MicrosoftTeamsIcon,
microsoft_planner: MicrosoftPlannerIcon,
microsoft_excel: MicrosoftExcelIcon,
memory: BrainIcon,
mem0: Mem0Icon,
mailgun: MailgunIcon,
mailchimp: MailchimpIcon,
linkup: LinkupIcon,
linkedin: LinkedInIcon,
linear: LinearIcon,
knowledge: PackageSearchIcon,
kalshi: KalshiIcon,
jira: JiraIcon,
jina: JinaAIIcon,
intercom: IntercomIcon,
incidentio: IncidentioIcon,
image_generator: ImageIcon,
hunter: HunterIOIcon,
huggingface: HuggingFaceIcon,
hubspot: HubspotIcon,
grafana: GrafanaIcon,
google_vault: GoogleVaultIcon,
google_slides: GoogleSlidesIcon,
google_sheets: GoogleSheetsIcon,
google_groups: GoogleGroupsIcon,
google_forms: GoogleFormsIcon,
google_drive: GoogleDriveIcon,
google_docs: GoogleDocsIcon,
google_calendar: GoogleCalendarIcon,
google_search: GoogleIcon,
gmail: GmailIcon,
gitlab: GitLabIcon,
github: GithubIcon,
firecrawl: FirecrawlIcon,
file: DocumentIcon,
exa: ExaAIIcon,
elevenlabs: ElevenLabsIcon,
elasticsearch: ElasticsearchIcon,
dynamodb: DynamoDBIcon,
duckduckgo: DuckDuckGoIcon,
dropbox: DropboxIcon,
discord: DiscordIcon,
datadog: DatadogIcon,
cursor: CursorIcon,
confluence: ConfluenceIcon,
clay: ClayIcon,
calendly: CalendlyIcon,
browser_use: BrowserUseIcon,
asana: AsanaIcon,
arxiv: ArxivIcon,
apollo: ApolloIcon,
apify: ApifyIcon,
airtable: AirtableIcon,
a2a: A2AIcon,
ahrefs: AhrefsIcon,
airtable: AirtableIcon,
airweave: AirweaveIcon,
algolia: AlgoliaIcon,
amplitude: AmplitudeIcon,
apify: ApifyIcon,
apollo: ApolloIcon,
arxiv: ArxivIcon,
asana: AsanaIcon,
ashby: AshbyIcon,
attio: AttioIcon,
brandfetch: BrandfetchIcon,
browser_use: BrowserUseIcon,
calcom: CalComIcon,
calendly: CalendlyIcon,
circleback: CirclebackIcon,
clay: ClayIcon,
clerk: ClerkIcon,
cloudflare: CloudflareIcon,
confluence_v2: ConfluenceIcon,
cursor_v2: CursorIcon,
databricks: DatabricksIcon,
datadog: DatadogIcon,
devin: DevinIcon,
discord: DiscordIcon,
dropbox: DropboxIcon,
dspy: DsPyIcon,
dub: DubIcon,
duckduckgo: DuckDuckGoIcon,
dynamodb: DynamoDBIcon,
elasticsearch: ElasticsearchIcon,
elevenlabs: ElevenLabsIcon,
enrich: EnrichSoIcon,
evernote: EvernoteIcon,
exa: ExaAIIcon,
fathom: FathomIcon,
file_v3: DocumentIcon,
firecrawl: FirecrawlIcon,
fireflies_v2: FirefliesIcon,
gamma: GammaIcon,
github_v2: GithubIcon,
gitlab: GitLabIcon,
gmail_v2: GmailIcon,
gong: GongIcon,
google_ads: GoogleAdsIcon,
google_bigquery: GoogleBigQueryIcon,
google_books: GoogleBooksIcon,
google_calendar_v2: GoogleCalendarIcon,
google_contacts: GoogleContactsIcon,
google_docs: GoogleDocsIcon,
google_drive: GoogleDriveIcon,
google_forms: GoogleFormsIcon,
google_groups: GoogleGroupsIcon,
google_maps: GoogleMapsIcon,
google_meet: GoogleMeetIcon,
google_pagespeed: GooglePagespeedIcon,
google_search: GoogleIcon,
google_sheets_v2: GoogleSheetsIcon,
google_slides_v2: GoogleSlidesIcon,
google_tasks: GoogleTasksIcon,
google_translate: GoogleTranslateIcon,
google_vault: GoogleVaultIcon,
grafana: GrafanaIcon,
grain: GrainIcon,
greenhouse: GreenhouseIcon,
greptile: GreptileIcon,
hex: HexIcon,
hubspot: HubspotIcon,
huggingface: HuggingFaceIcon,
hunter: HunterIOIcon,
image_generator: ImageIcon,
imap: MailServerIcon,
incidentio: IncidentioIcon,
intercom_v2: IntercomIcon,
jina: JinaAIIcon,
jira: JiraIcon,
jira_service_management: JiraServiceManagementIcon,
kalshi_v2: KalshiIcon,
knowledge: PackageSearchIcon,
langsmith: LangsmithIcon,
lemlist: LemlistIcon,
linear: LinearIcon,
linkedin: LinkedInIcon,
linkup: LinkupIcon,
loops: LoopsIcon,
luma: LumaIcon,
mailchimp: MailchimpIcon,
mailgun: MailgunIcon,
mem0: Mem0Icon,
memory: BrainIcon,
microsoft_dataverse: MicrosoftDataverseIcon,
microsoft_excel_v2: MicrosoftExcelIcon,
microsoft_planner: MicrosoftPlannerIcon,
microsoft_teams: MicrosoftTeamsIcon,
mistral_parse_v3: MistralIcon,
mongodb: MongoDBIcon,
mysql: MySQLIcon,
neo4j: Neo4jIcon,
notion_v2: NotionIcon,
obsidian: ObsidianIcon,
onedrive: MicrosoftOneDriveIcon,
onepassword: OnePasswordIcon,
openai: OpenAIIcon,
outlook: OutlookIcon,
pagerduty: PagerDutyIcon,
parallel_ai: ParallelIcon,
perplexity: PerplexityIcon,
pinecone: PineconeIcon,
pipedrive: PipedriveIcon,
polymarket: PolymarketIcon,
postgresql: PostgresIcon,
posthog: PosthogIcon,
pulse_v2: PulseIcon,
qdrant: QdrantIcon,
rds: RDSIcon,
reddit: RedditIcon,
redis: RedisIcon,
reducto_v2: ReductoIcon,
resend: ResendIcon,
revenuecat: RevenueCatIcon,
s3: S3Icon,
salesforce: SalesforceIcon,
search: SearchIcon,
sendgrid: SendgridIcon,
sentry: SentryIcon,
serper: SerperIcon,
servicenow: ServiceNowIcon,
sftp: SftpIcon,
sharepoint: MicrosoftSharepointIcon,
shopify: ShopifyIcon,
similarweb: SimilarwebIcon,
slack: SlackIcon,
smtp: SmtpIcon,
sqs: SQSIcon,
ssh: SshIcon,
stagehand: StagehandIcon,
stripe: StripeIcon,
stt_v2: STTIcon,
supabase: SupabaseIcon,
tavily: TavilyIcon,
telegram: TelegramIcon,
textract_v2: TextractIcon,
tinybird: TinybirdIcon,
translate: TranslateIcon,
trello: TrelloIcon,
tts: TTSIcon,
twilio_sms: TwilioIcon,
twilio_voice: TwilioIcon,
typeform: TypeformIcon,
upstash: UpstashIcon,
vercel: VercelIcon,
video_generator_v2: VideoIcon,
vision_v2: EyeIcon,
wealthbox: WealthboxIcon,
webflow: WebflowIcon,
whatsapp: WhatsAppIcon,
wikipedia: WikipediaIcon,
wordpress: WordpressIcon,
x: xIcon,
youtube: YouTubeIcon,
zendesk: ZendeskIcon,
zep: ZepIcon,
zoom: ZoomIcon,
}

View File

@@ -30,7 +30,7 @@ export function Image({
<NextImage
className={cn(
'overflow-hidden rounded-xl border border-border object-cover shadow-sm',
enableLightbox && 'cursor-pointer transition-opacity hover:opacity-90',
enableLightbox && 'cursor-pointer transition-opacity hover:opacity-95',
className
)}
alt={alt}

View File

@@ -1,8 +1,9 @@
'use client'
import { useEffect, useState } from 'react'
import { Check, ChevronRight } from 'lucide-react'
import { Check, ChevronDown } from 'lucide-react'
import { useParams, usePathname, useRouter } from 'next/navigation'
import { cn } from '@/lib/utils'
const languages = {
en: { name: 'English', flag: '🇺🇸' },
@@ -15,6 +16,7 @@ const languages = {
export function LanguageDropdown() {
const [isOpen, setIsOpen] = useState(false)
const [hoveredIndex, setHoveredIndex] = useState<number>(-1)
const pathname = usePathname()
const params = useParams()
const router = useRouter()
@@ -71,6 +73,15 @@ export function LanguageDropdown() {
return () => window.removeEventListener('keydown', onKey)
}, [isOpen])
// Reset hovered index when popover closes
useEffect(() => {
if (!isOpen) {
setHoveredIndex(-1)
}
}, [isOpen])
const languageEntries = Object.entries(languages)
return (
<div className='relative'>
<button
@@ -82,14 +93,14 @@ export function LanguageDropdown() {
aria-haspopup='listbox'
aria-expanded={isOpen}
aria-controls='language-menu'
className='flex cursor-pointer items-center gap-1.5 rounded-xl px-3 py-2 font-normal text-[0.9375rem] text-foreground/60 leading-[1.4] transition-colors hover:bg-foreground/8 hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring'
className='flex cursor-pointer items-center gap-1.5 rounded-[6px] px-3 py-2 font-normal text-[0.9375rem] text-foreground/60 leading-[1.4] transition-colors hover:bg-foreground/8 hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring'
style={{
fontFamily:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
}}
>
<span>{languages[currentLang as keyof typeof languages]?.name}</span>
<ChevronRight className='h-3.5 w-3.5' />
<ChevronDown className={cn('h-3.5 w-3.5 transition-transform', isOpen && 'rotate-180')} />
</button>
{isOpen && (
@@ -98,29 +109,37 @@ export function LanguageDropdown() {
<div
id='language-menu'
role='listbox'
className='absolute top-full right-0 z-[1001] mt-1 max-h-[75vh] w-56 overflow-auto rounded-xl border border-border/50 bg-white shadow-2xl md:w-44 md:bg-background/95 md:backdrop-blur-md dark:bg-neutral-950 md:dark:bg-background/95'
className='absolute top-full right-0 z-[1001] mt-2 max-h-[400px] min-w-[160px] overflow-auto rounded-[6px] bg-white px-[6px] py-[6px] shadow-lg dark:bg-neutral-900'
>
{Object.entries(languages).map(([code, lang]) => (
<button
key={code}
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
handleLanguageChange(code)
}}
role='option'
aria-selected={currentLang === code}
className={`flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-base transition-colors first:rounded-t-xl last:rounded-b-xl hover:bg-muted/80 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring md:gap-2 md:px-2.5 md:py-2 md:text-sm ${
currentLang === code ? 'bg-muted/60 font-medium text-primary' : 'text-foreground'
}`}
>
<span className='text-base md:text-sm'>{lang.flag}</span>
<span className='leading-none'>{lang.name}</span>
{currentLang === code && (
<Check className='ml-auto h-4 w-4 text-primary md:h-3.5 md:w-3.5' />
)}
</button>
))}
{languageEntries.map(([code, lang], index) => {
const isSelected = currentLang === code
const isHovered = hoveredIndex === index
return (
<button
key={code}
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
handleLanguageChange(code)
}}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(-1)}
role='option'
aria-selected={isSelected}
className={cn(
'flex h-[26px] w-full min-w-0 cursor-pointer items-center gap-[8px] rounded-[6px] px-[6px] text-[13px] transition-colors',
'text-neutral-700 dark:text-neutral-200',
isHovered && 'bg-neutral-100 dark:bg-neutral-800',
'focus:outline-none'
)}
>
<span className='text-[13px]'>{lang.flag}</span>
<span className='flex-1 text-left leading-none'>{lang.name}</span>
{isSelected && <Check className='ml-auto h-3.5 w-3.5' />}
</button>
)
})}
</div>
</>
)}

View File

@@ -55,8 +55,9 @@ export function Lightbox({ isOpen, onClose, src, alt, type }: LightboxProps) {
<img
src={src}
alt={alt}
className='max-h-[calc(100vh-6rem)] max-w-[calc(100vw-6rem)] rounded-xl object-contain'
className='max-h-[75vh] max-w-[75vw] cursor-pointer rounded-xl object-contain'
loading='lazy'
onClick={onClose}
/>
) : (
<video
@@ -65,7 +66,8 @@ export function Lightbox({ isOpen, onClose, src, alt, type }: LightboxProps) {
loop
muted
playsInline
className='max-h-[calc(100vh-6rem)] max-w-[calc(100vw-6rem)] rounded-xl outline-none focus:outline-none'
className='max-h-[75vh] max-w-[75vw] cursor-pointer rounded-xl outline-none focus:outline-none'
onClick={onClose}
/>
)}
</div>

View File

@@ -0,0 +1,169 @@
'use client'
import { useEffect, useRef, useState } from 'react'
import { ChevronDown } from 'lucide-react'
import { cn } from '@/lib/utils'
interface ResponseSectionProps {
children: React.ReactNode
}
export function ResponseSection({ children }: ResponseSectionProps) {
const containerRef = useRef<HTMLDivElement>(null)
const [statusCodes, setStatusCodes] = useState<string[]>([])
const [selectedCode, setSelectedCode] = useState<string>('')
const [isOpen, setIsOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
function getAccordionItems() {
const root = containerRef.current?.querySelector('[data-orientation="vertical"]')
if (!root) return []
return Array.from(root.children).filter(
(el) => el.getAttribute('data-state') !== null
) as HTMLElement[]
}
function showStatusCode(code: string) {
const items = getAccordionItems()
for (const item of items) {
const triggerBtn = item.querySelector('h3 button') as HTMLButtonElement | null
const text = triggerBtn?.textContent?.trim() ?? ''
const itemCode = text.match(/^\d{3}/)?.[0]
if (itemCode === code) {
item.style.display = ''
if (item.getAttribute('data-state') === 'closed' && triggerBtn) {
triggerBtn.click()
}
} else {
item.style.display = 'none'
if (item.getAttribute('data-state') === 'open' && triggerBtn) {
triggerBtn.click()
}
}
}
}
/**
* Detect when the fumadocs accordion children mount via MutationObserver,
* then extract status codes and show the first one.
* Replaces the previous approach that used `children` as a dependency
* (which triggered on every render since children is a new object each time).
*/
useEffect(() => {
const container = containerRef.current
if (!container) return
const initialize = () => {
const items = getAccordionItems()
if (items.length === 0) return false
const codes: string[] = []
const seen = new Set<string>()
for (const item of items) {
const triggerBtn = item.querySelector('h3 button')
if (triggerBtn) {
const text = triggerBtn.textContent?.trim() ?? ''
const code = text.match(/^\d{3}/)?.[0]
if (code && !seen.has(code)) {
seen.add(code)
codes.push(code)
}
}
}
if (codes.length > 0) {
setStatusCodes(codes)
setSelectedCode(codes[0])
showStatusCode(codes[0])
return true
}
return false
}
if (initialize()) return
const observer = new MutationObserver(() => {
if (initialize()) {
observer.disconnect()
}
})
observer.observe(container, { childList: true, subtree: true })
return () => observer.disconnect()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
function handleSelectCode(code: string) {
setSelectedCode(code)
setIsOpen(false)
showStatusCode(code)
}
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [])
return (
<div ref={containerRef} className='response-section-wrapper'>
{statusCodes.length > 0 && (
<div className='response-section-header'>
<h2 className='response-section-title'>Response</h2>
<div className='response-section-meta'>
<div ref={dropdownRef} className='response-section-dropdown-wrapper'>
<button
type='button'
className='response-section-dropdown-trigger'
onClick={() => setIsOpen(!isOpen)}
>
<span>{selectedCode}</span>
<ChevronDown
className={cn(
'response-section-chevron',
isOpen && 'response-section-chevron-open'
)}
/>
</button>
{isOpen && (
<div className='response-section-dropdown-menu'>
{statusCodes.map((code) => (
<button
key={code}
type='button'
className={cn(
'response-section-dropdown-item',
code === selectedCode && 'response-section-dropdown-item-selected'
)}
onClick={() => handleSelectCode(code)}
>
<span>{code}</span>
{code === selectedCode && (
<svg
className='response-section-check'
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
strokeWidth='2'
>
<polyline points='20 6 9 17 4 12' />
</svg>
)}
</button>
))}
</div>
)}
</div>
<span className='response-section-content-type'>application/json</span>
</div>
</div>
)}
<div className='response-section-content'>{children}</div>
</div>
)
}

View File

@@ -15,23 +15,14 @@ export function SearchTrigger() {
return (
<button
type='button'
className='flex h-10 w-[460px] cursor-pointer items-center gap-2 rounded-xl border border-border/50 px-3 py-2 text-sm backdrop-blur-xl transition-colors hover:border-border'
style={{
backgroundColor: 'hsla(0, 0%, 5%, 0.85)',
backdropFilter: 'blur(33px) saturate(180%)',
WebkitBackdropFilter: 'blur(33px) saturate(180%)',
color: 'rgba(255, 255, 255, 0.6)',
}}
className='flex h-9 w-[360px] cursor-pointer items-center gap-2 rounded-lg border border-border/50 bg-fd-muted/50 px-3 text-[13px] text-fd-muted-foreground transition-colors hover:border-border hover:text-fd-foreground'
onClick={handleClick}
>
<Search className='h-4 w-4' />
<Search className='h-3.5 w-3.5' />
<span>Search...</span>
<kbd
className='ml-auto flex items-center gap-0.5 font-medium'
style={{ color: 'rgba(255, 255, 255, 0.6)' }}
>
<span style={{ fontSize: '15px', lineHeight: '1' }}></span>
<span style={{ fontSize: '13px', lineHeight: '1' }}>K</span>
<kbd className='ml-auto flex items-center font-medium'>
<span className='text-[15px]'></span>
<span className='text-[12px]'>K</span>
</kbd>
</button>
)

View File

@@ -0,0 +1,108 @@
'use client'
import { cn } from '@/lib/utils'
interface SimLogoProps {
className?: string
}
/**
* Sim logo with icon and text.
* The icon stays green (#33C482), text adapts to light/dark mode.
*/
export function SimLogo({ className }: SimLogoProps) {
return (
<svg
viewBox='720 440 320 320'
fill='none'
xmlns='http://www.w3.org/2000/svg'
className={cn('h-7 w-auto', className)}
aria-label='Sim'
>
{/* Green icon - top left shape with cutout */}
<path
fillRule='evenodd'
clipRule='evenodd'
d='M875.791 577.171C875.791 581.922 873.911 586.483 870.576 589.842L870.098 590.323C866.764 593.692 862.234 595.575 857.517 595.575H750.806C740.978 595.575 733 603.6 733 613.498V728.902C733 738.799 740.978 746.826 750.806 746.826H865.382C875.209 746.826 883.177 738.799 883.177 728.902V620.853C883.177 616.448 884.912 612.222 888.008 609.104C891.093 605.997 895.29 604.249 899.664 604.249H1008.16C1017.99 604.249 1025.96 596.224 1025.96 586.327V470.923C1025.96 461.025 1017.99 453 1008.16 453H893.586C883.759 453 875.791 461.025 875.791 470.923V577.171ZM910.562 477.566H991.178C996.922 477.566 1001.57 482.254 1001.57 488.029V569.22C1001.57 574.995 996.922 579.683 991.178 579.683H910.562C904.828 579.683 900.173 574.995 900.173 569.22V488.029C900.173 482.254 904.828 477.566 910.562 477.566Z'
fill='#33C482'
/>
{/* Green icon - bottom right square */}
<path
d='M1008.3 624.59H923.113C912.786 624.59 904.414 633.022 904.414 643.423V728.171C904.414 738.572 912.786 747.004 923.113 747.004H1008.3C1018.63 747.004 1027 738.572 1027 728.171V643.423C1027 633.022 1018.63 624.59 1008.3 624.59Z'
fill='#33C482'
/>
{/* Gradient overlay on bottom right square */}
<path
d='M1008.3 624.199H923.113C912.786 624.199 904.414 632.631 904.414 643.033V727.78C904.414 738.181 912.786 746.612 923.113 746.612H1008.3C1018.63 746.612 1027 738.181 1027 727.78V643.033C1027 632.631 1018.63 624.199 1008.3 624.199Z'
fill='url(#sim-logo-gradient)'
fillOpacity='0.2'
/>
<defs>
<linearGradient
id='sim-logo-gradient'
x1='904.414'
y1='624.199'
x2='978.836'
y2='698.447'
gradientUnits='userSpaceOnUse'
>
<stop />
<stop offset='1' stopOpacity='0' />
</linearGradient>
</defs>
</svg>
)
}
/**
* Full Sim logo with icon and "Sim" text.
* The icon stays green (#33C482), text adapts to light/dark mode.
*/
export function SimLogoFull({ className }: SimLogoProps) {
return (
<svg
viewBox='720 440 1020 320'
fill='none'
xmlns='http://www.w3.org/2000/svg'
className={cn('h-7 w-auto', className)}
aria-label='Sim'
>
{/* Green icon - top left shape with cutout */}
<path
fillRule='evenodd'
clipRule='evenodd'
d='M875.791 577.171C875.791 581.922 873.911 586.483 870.576 589.842L870.098 590.323C866.764 593.692 862.234 595.575 857.517 595.575H750.806C740.978 595.575 733 603.6 733 613.498V728.902C733 738.799 740.978 746.826 750.806 746.826H865.382C875.209 746.826 883.177 738.799 883.177 728.902V620.853C883.177 616.448 884.912 612.222 888.008 609.104C891.093 605.997 895.29 604.249 899.664 604.249H1008.16C1017.99 604.249 1025.96 596.224 1025.96 586.327V470.923C1025.96 461.025 1017.99 453 1008.16 453H893.586C883.759 453 875.791 461.025 875.791 470.923V577.171ZM910.562 477.566H991.178C996.922 477.566 1001.57 482.254 1001.57 488.029V569.22C1001.57 574.995 996.922 579.683 991.178 579.683H910.562C904.828 579.683 900.173 574.995 900.173 569.22V488.029C900.173 482.254 904.828 477.566 910.562 477.566Z'
fill='#33C482'
/>
{/* Green icon - bottom right square */}
<path
d='M1008.3 624.59H923.113C912.786 624.59 904.414 633.022 904.414 643.423V728.171C904.414 738.572 912.786 747.004 923.113 747.004H1008.3C1018.63 747.004 1027 738.572 1027 728.171V643.423C1027 633.022 1018.63 624.59 1008.3 624.59Z'
fill='#33C482'
/>
{/* Gradient overlay on bottom right square */}
<path
d='M1008.3 624.199H923.113C912.786 624.199 904.414 632.631 904.414 643.033V727.78C904.414 738.181 912.786 746.612 923.113 746.612H1008.3C1018.63 746.612 1027 738.181 1027 727.78V643.033C1027 632.631 1018.63 624.199 1008.3 624.199Z'
fill='url(#sim-logo-full-gradient)'
fillOpacity='0.2'
/>
{/* "Sim" text - adapts to light/dark mode via currentColor */}
<path
d='M1210.54 515.657C1226.65 515.657 1240.59 518.51 1252.31 524.257H1252.31C1264.3 529.995 1273.63 538.014 1280.26 548.319H1280.26C1287.19 558.635 1290.78 570.899 1291.08 585.068L1291.1 586.089H1249.11L1249.09 585.115C1248.8 574.003 1245.18 565.493 1238.32 559.451C1231.45 553.399 1221.79 550.308 1209.21 550.308C1196.3 550.308 1186.48 553.113 1179.61 558.588C1172.76 564.046 1169.33 571.499 1169.33 581.063C1169.33 588.092 1171.88 593.978 1177.01 598.783C1182.17 603.618 1189.99 607.399 1200.56 610.061H1200.56L1238.77 619.451C1257.24 623.65 1271.21 630.571 1280.57 640.293L1281.01 640.739C1290.13 650.171 1294.64 662.97 1294.64 679.016C1294.64 692.923 1290.88 705.205 1283.34 715.822L1283.33 715.834C1275.81 726.134 1265.44 734.14 1252.26 739.866L1252.25 739.871C1239.36 745.302 1224.12 748 1206.54 748C1180.9 748 1160.36 741.696 1145.02 728.984C1129.67 716.258 1122 699.269 1122 678.121V677.121H1163.99V678.121C1163.99 688.869 1167.87 697.367 1175.61 703.722L1176.34 704.284C1184.04 709.997 1194.37 712.902 1207.43 712.902C1222.13 712.902 1233.3 710.087 1241.07 704.588C1248.8 698.812 1252.64 691.21 1252.64 681.699C1252.64 674.769 1250.5 669.057 1246.25 664.49L1246.23 664.478L1246.22 664.464C1242.28 659.929 1234.83 656.119 1223.64 653.152L1185.43 644.208L1185.42 644.204C1166.05 639.407 1151.49 632.035 1141.83 622.012L1141.83 622.006L1141.82 622C1132.43 611.94 1127.78 598.707 1127.78 582.405C1127.78 568.81 1131.23 556.976 1138.17 546.949L1138.18 546.941L1138.19 546.933C1145.41 536.936 1155.18 529.225 1167.48 523.793L1167.48 523.79C1180.07 518.36 1194.43 515.657 1210.54 515.657ZM1323.39 521.979C1331.68 525.008 1337.55 526.482 1343.51 526.482C1349.48 526.482 1355.64 525.005 1364.49 521.973L1365.82 521.52V742.633H1322.05V521.489L1323.39 521.979ZM1642.01 515.657C1667.11 515.657 1686.94 523.031 1701.39 537.876C1715.83 552.716 1723 572.968 1723 598.507V742.633H1680.12V608.794C1680.12 591.666 1675.72 578.681 1667.07 569.681L1667.06 569.669L1667.04 569.656C1658.67 560.359 1647.26 555.675 1632.68 555.675C1622.47 555.675 1613.47 558.022 1605.64 562.69L1605.63 562.696C1598.11 567.064 1592.17 573.475 1587.8 581.968C1583.44 590.448 1581.25 600.424 1581.25 611.925V742.633H1537.92V608.347C1537.92 591.208 1533.67 578.376 1525.31 569.68L1525.31 569.674L1525.3 569.668C1516.93 560.664 1505.52 556.122 1490.93 556.122C1480.72 556.122 1471.72 558.469 1463.89 563.138L1463.88 563.144C1456.36 567.511 1450.41 573.922 1446.05 582.415L1446.05 582.422L1446.04 582.428C1441.69 590.602 1439.5 600.423 1439.5 611.925V742.633H1395.72V521.919H1435.05V554.803C1439.92 544.379 1447.91 535.465 1458.37 528.356C1470.71 519.875 1485.58 515.657 1502.93 515.657C1522.37 515.657 1538.61 520.931 1551.55 531.538C1560.38 538.771 1567.1 547.628 1571.72 558.091C1576.05 547.619 1582.83 538.757 1592.07 531.524C1605.61 520.93 1622.28 515.657 1642.01 515.657ZM1343.49 452C1351.45 452 1358.23 454.786 1363.75 460.346C1369.27 465.905 1372.04 472.721 1372.04 480.73C1372.04 488.452 1369.27 495.254 1363.77 501.096L1363.76 501.105L1363.75 501.115C1358.23 506.675 1351.45 509.461 1343.49 509.461C1335.81 509.461 1329.05 506.669 1323.25 501.134L1323.23 501.115L1323.21 501.096C1317.71 495.254 1314.94 488.452 1314.94 480.73C1314.94 472.721 1317.7 465.905 1323.23 460.346L1323.24 460.337L1323.25 460.327C1329.05 454.792 1335.81 452 1343.49 452Z'
className='fill-neutral-900 dark:fill-white'
/>
<defs>
<linearGradient
id='sim-logo-full-gradient'
x1='904.414'
y1='624.199'
x2='978.836'
y2='698.447'
gradientUnits='userSpaceOnUse'
>
<stop />
<stop offset='1' stopOpacity='0' />
</linearGradient>
</defs>
</svg>
)
}

View File

@@ -38,7 +38,7 @@ export function Video({
loop={loop}
muted={muted}
playsInline={playsInline}
className={`${className} ${enableLightbox ? 'cursor-pointer transition-opacity hover:opacity-90' : ''}`}
className={`${className} ${enableLightbox ? 'cursor-pointer transition-opacity hover:opacity-95' : ''}`}
src={getAssetUrl(src)}
onClick={handleVideoClick}
/>

View File

@@ -0,0 +1,94 @@
---
title: Authentication
description: API key types, generation, and how to authenticate requests
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
To access the Sim API, you need an API key. Sim supports two types of API keys — **personal keys** and **workspace keys** — each with different billing and access behaviors.
## Key Types
| | **Personal Keys** | **Workspace Keys** |
| --- | --- | --- |
| **Billed to** | Your individual account | Workspace owner |
| **Scope** | Across workspaces you have access to | Shared across the workspace |
| **Managed by** | Each user individually | Workspace admins |
| **Permissions** | Must be enabled at workspace level | Require admin permissions |
<Callout type="info">
Workspace admins can disable personal API key usage for their workspace. If disabled, only workspace keys can be used.
</Callout>
## Generating API Keys
To generate a key, open the Sim dashboard and navigate to **Settings**, then go to **Sim Keys** and click **Create**.
<Callout type="warn">
API keys are only shown once when generated. Store your key securely — you will not be able to view it again.
</Callout>
## Using API Keys
Pass your API key in the `X-API-Key` header with every request:
<Tabs items={['curl', 'TypeScript', 'Python']}>
<Tab value="curl">
```bash
curl -X POST https://www.sim.ai/api/workflows/{workflowId}/execute \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"inputs": {}}'
```
</Tab>
<Tab value="TypeScript">
```typescript
const response = await fetch(
'https://www.sim.ai/api/workflows/{workflowId}/execute',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.SIM_API_KEY!,
},
body: JSON.stringify({ inputs: {} }),
}
)
```
</Tab>
<Tab value="Python">
```python
import requests
response = requests.post(
"https://www.sim.ai/api/workflows/{workflowId}/execute",
headers={
"Content-Type": "application/json",
"X-API-Key": os.environ["SIM_API_KEY"],
},
json={"inputs": {}},
)
```
</Tab>
</Tabs>
## Where Keys Are Used
API keys authenticate access to:
- **Workflow execution** — run deployed workflows via the API
- **Logs API** — query workflow execution logs and metrics
- **MCP servers** — authenticate connections to deployed MCP servers
- **SDKs** — the [Python](/api-reference/python) and [TypeScript](/api-reference/typescript) SDKs use API keys for all operations
## Security
- Keys use the `sk-sim-` prefix and are encrypted at rest
- Keys can be revoked at any time from the dashboard
- Use environment variables to store keys — never hardcode them in source code
- For browser-based applications, use a backend proxy to avoid exposing keys to the client
<Callout type="warn">
Never expose your API key in client-side code. Use a server-side proxy to make authenticated requests on behalf of your frontend.
</Callout>

View File

@@ -0,0 +1,210 @@
---
title: Getting Started
description: Base URL, first API call, response format, error handling, and pagination
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Step, Steps } from 'fumadocs-ui/components/steps'
## Base URL
All API requests are made to:
```
https://www.sim.ai
```
## Quick Start
<Steps>
<Step>
### Get your API key
Go to the Sim dashboard and navigate to **Settings → Sim Keys**, then click **Create**. See [Authentication](/api-reference/authentication) for details on key types.
</Step>
<Step>
### Find your workflow ID
Open a workflow in the Sim editor. The workflow ID is in the URL:
```
https://www.sim.ai/workspace/{workspaceId}/w/{workflowId}
```
You can also use the [List Workflows](/api-reference/workflows/listWorkflows) endpoint to get all workflow IDs in a workspace.
</Step>
<Step>
### Deploy your workflow
A workflow must be deployed before it can be executed via the API. Click the **Deploy** button in the editor toolbar.
</Step>
<Step>
### Make your first request
<Tabs items={['curl', 'TypeScript', 'Python']}>
<Tab value="curl">
```bash
curl -X POST https://www.sim.ai/api/workflows/{workflowId}/execute \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"inputs": {}}'
```
</Tab>
<Tab value="TypeScript">
```typescript
const response = await fetch(
`https://www.sim.ai/api/workflows/${workflowId}/execute`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.SIM_API_KEY!,
},
body: JSON.stringify({ inputs: {} }),
}
)
const data = await response.json()
console.log(data.output)
```
</Tab>
<Tab value="Python">
```python
import requests
import os
response = requests.post(
f"https://www.sim.ai/api/workflows/{workflow_id}/execute",
headers={
"Content-Type": "application/json",
"X-API-Key": os.environ["SIM_API_KEY"],
},
json={"inputs": {}},
)
data = response.json()
print(data["output"])
```
</Tab>
</Tabs>
</Step>
</Steps>
## Sync vs Async Execution
By default, workflow executions are **synchronous** — the API blocks until the workflow completes and returns the result directly.
For long-running workflows, use **asynchronous execution** by passing `async: true`:
```bash
curl -X POST https://www.sim.ai/api/workflows/{workflowId}/execute \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"inputs": {}, "async": true}'
```
This returns immediately with a `taskId`:
```json
{
"success": true,
"taskId": "job_abc123",
"status": "queued"
}
```
Poll the [Get Job Status](/api-reference/workflows/getJobStatus) endpoint until the status is `completed` or `failed`:
```bash
curl https://www.sim.ai/api/jobs/{taskId} \
-H "X-API-Key: YOUR_API_KEY"
```
<Callout type="info">
Job status transitions follow: `queued` → `processing` → `completed` or `failed`. The `output` field is only present when status is `completed`.
</Callout>
## Response Format
Successful responses include an `output` object with your workflow results and a `limits` object with your current rate limit and usage status:
```json
{
"success": true,
"output": {
"result": "Hello, world!"
},
"limits": {
"workflowExecutionRateLimit": {
"sync": {
"requestsPerMinute": 60,
"maxBurst": 10,
"remaining": 59,
"resetAt": "2025-01-01T00:01:00Z"
},
"async": {
"requestsPerMinute": 30,
"maxBurst": 5,
"remaining": 30,
"resetAt": "2025-01-01T00:01:00Z"
}
},
"usage": {
"currentPeriodCost": 1.25,
"limit": 50.00,
"plan": "pro",
"isExceeded": false
}
}
}
```
## Error Handling
The API uses standard HTTP status codes. Error responses include a human-readable `error` message:
```json
{
"error": "Workflow not found"
}
```
| Status | Meaning | What to do |
| --- | --- | --- |
| `400` | Invalid request parameters | Check the `details` array for specific field errors |
| `401` | Missing or invalid API key | Verify your `X-API-Key` header |
| `403` | Access denied | Check you have permission for this resource |
| `404` | Resource not found | Verify the ID exists and belongs to your workspace |
| `429` | Rate limit exceeded | Wait for the duration in the `Retry-After` header |
<Callout type="info">
Use the [Get Usage Limits](/api-reference/usage/getUsageLimits) endpoint to check your current rate limit status and billing usage at any time.
</Callout>
## Rate Limits
Rate limits depend on your subscription plan and apply separately to synchronous and asynchronous executions. Every execution response includes a `limits` object showing your current rate limit status.
When rate limited, the API returns a `429` response with a `Retry-After` header indicating how many seconds to wait before retrying.
## Pagination
List endpoints (workflows, logs, audit logs) use **cursor-based pagination**:
```bash
# First page
curl "https://www.sim.ai/api/v1/logs?limit=20" \
-H "X-API-Key: YOUR_API_KEY"
# Next page — use the nextCursor from the previous response
curl "https://www.sim.ai/api/v1/logs?limit=20&cursor=abc123" \
-H "X-API-Key: YOUR_API_KEY"
```
The response includes a `nextCursor` field. When `nextCursor` is absent or `null`, you have reached the last page.

View File

@@ -0,0 +1,18 @@
{
"title": "API Reference",
"root": true,
"pages": [
"getting-started",
"authentication",
"---SDKs---",
"python",
"typescript",
"---Endpoints---",
"(generated)/workflows",
"(generated)/logs",
"(generated)/usage",
"(generated)/audit-logs",
"(generated)/tables",
"(generated)/files"
]
}

View File

@@ -0,0 +1,766 @@
---
title: Python
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Card, Cards } from 'fumadocs-ui/components/card'
import { Step, Steps } from 'fumadocs-ui/components/steps'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
Das offizielle Python SDK für Sim ermöglicht es Ihnen, Workflows programmatisch aus Ihren Python-Anwendungen heraus mit dem offiziellen Python SDK auszuführen.
<Callout type="info">
Das Python SDK unterstützt Python 3.8+ mit Unterstützung für asynchrone Ausführung, automatischer Ratenbegrenzung mit exponentiellem Backoff und Nutzungsverfolgung.
</Callout>
## Installation
Installieren Sie das SDK mit pip:
```bash
pip install simstudio-sdk
```
## Schnellstart
Hier ist ein einfaches Beispiel für den Einstieg:
```python
from simstudio import SimStudioClient
# Initialize the client
client = SimStudioClient(
api_key="your-api-key-here",
base_url="https://sim.ai" # optional, defaults to https://sim.ai
)
# Execute a workflow
try:
result = client.execute_workflow("workflow-id")
print("Workflow executed successfully:", result)
except Exception as error:
print("Workflow execution failed:", error)
```
## API-Referenz
### SimStudioClient
#### Konstruktor
```python
SimStudioClient(api_key: str, base_url: str = "https://sim.ai")
```
**Parameter:**
- `api_key` (str): Ihr Sim API-Schlüssel
- `base_url` (str, optional): Basis-URL für die Sim API
#### Methoden
##### execute_workflow()
Führt einen Workflow mit optionalen Eingabedaten aus.
```python
result = client.execute_workflow(
"workflow-id",
input_data={"message": "Hello, world!"},
timeout=30.0 # 30 seconds
)
```
**Parameter:**
- `workflow_id` (str): Die ID des auszuführenden Workflows
- `input_data` (dict, optional): Eingabedaten, die an den Workflow übergeben werden
- `timeout` (float, optional): Timeout in Sekunden (Standard: 30.0)
- `stream` (bool, optional): Streaming-Antworten aktivieren (Standard: False)
- `selected_outputs` (list[str], optional): Block-Ausgaben zum Streamen im Format `blockName.attribute` (z. B. `["agent1.content"]`)
- `async_execution` (bool, optional): Asynchron ausführen (Standard: False)
**Rückgabewert:** `WorkflowExecutionResult | AsyncExecutionResult`
Wenn `async_execution=True`, wird sofort mit einer Task-ID zum Polling zurückgegeben. Andernfalls wird auf die Fertigstellung gewartet.
##### get_workflow_status()
Ruft den Status eines Workflows ab (Deployment-Status usw.).
```python
status = client.get_workflow_status("workflow-id")
print("Is deployed:", status.is_deployed)
```
**Parameter:**
- `workflow_id` (str): Die ID des Workflows
**Rückgabe:** `WorkflowStatus`
##### validate_workflow()
Überprüft, ob ein Workflow zur Ausführung bereit ist.
```python
is_ready = client.validate_workflow("workflow-id")
if is_ready:
# Workflow is deployed and ready
pass
```
**Parameter:**
- `workflow_id` (str): Die ID des Workflows
**Rückgabe:** `bool`
##### get_job_status()
Ruft den Status einer asynchronen Job-Ausführung ab.
```python
status = client.get_job_status("task-id-from-async-execution")
print("Status:", status["status"]) # 'queued', 'processing', 'completed', 'failed'
if status["status"] == "completed":
print("Output:", status["output"])
```
**Parameter:**
- `task_id` (str): Die Task-ID, die von der asynchronen Ausführung zurückgegeben wurde
**Rückgabe:** `Dict[str, Any]`
**Antwortfelder:**
- `success` (bool): Ob die Anfrage erfolgreich war
- `taskId` (str): Die Task-ID
- `status` (str): Einer von `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
- `metadata` (dict): Enthält `startedAt`, `completedAt` und `duration`
- `output` (any, optional): Die Workflow-Ausgabe (wenn abgeschlossen)
- `error` (any, optional): Fehlerdetails (wenn fehlgeschlagen)
- `estimatedDuration` (int, optional): Geschätzte Dauer in Millisekunden (wenn in Bearbeitung/in Warteschlange)
##### execute_with_retry()
Führt einen Workflow mit automatischer Wiederholung bei Rate-Limit-Fehlern unter Verwendung von exponentiellem Backoff aus.
```python
result = client.execute_with_retry(
"workflow-id",
input_data={"message": "Hello"},
timeout=30.0,
max_retries=3, # Maximum number of retries
initial_delay=1.0, # Initial delay in seconds
max_delay=30.0, # Maximum delay in seconds
backoff_multiplier=2.0 # Exponential backoff multiplier
)
```
**Parameter:**
- `workflow_id` (str): Die ID des auszuführenden Workflows
- `input_data` (dict, optional): Eingabedaten, die an den Workflow übergeben werden
- `timeout` (float, optional): Timeout in Sekunden
- `stream` (bool, optional): Streaming-Antworten aktivieren
- `selected_outputs` (list, optional): Block-Ausgaben zum Streamen
- `async_execution` (bool, optional): Asynchron ausführen
- `max_retries` (int, optional): Maximale Anzahl von Wiederholungen (Standard: 3)
- `initial_delay` (float, optional): Anfangsverzögerung in Sekunden (Standard: 1.0)
- `max_delay` (float, optional): Maximale Verzögerung in Sekunden (Standard: 30.0)
- `backoff_multiplier` (float, optional): Backoff-Multiplikator (Standard: 2.0)
**Rückgabe:** `WorkflowExecutionResult | AsyncExecutionResult`
Die Wiederholungslogik verwendet exponentielles Backoff (1s → 2s → 4s → 8s...) mit ±25% Jitter, um Thundering Herd zu verhindern. Wenn die API einen `retry-after`-Header bereitstellt, wird dieser stattdessen verwendet.
##### get_rate_limit_info()
Ruft die aktuellen Rate-Limit-Informationen aus der letzten API-Antwort ab.
```python
rate_limit_info = client.get_rate_limit_info()
if rate_limit_info:
print("Limit:", rate_limit_info.limit)
print("Remaining:", rate_limit_info.remaining)
print("Reset:", datetime.fromtimestamp(rate_limit_info.reset))
```
**Rückgabewert:** `RateLimitInfo | None`
##### get_usage_limits()
Ruft aktuelle Nutzungslimits und Kontingentinformationen für Ihr Konto ab.
```python
limits = client.get_usage_limits()
print("Sync requests remaining:", limits.rate_limit["sync"]["remaining"])
print("Async requests remaining:", limits.rate_limit["async"]["remaining"])
print("Current period cost:", limits.usage["currentPeriodCost"])
print("Plan:", limits.usage["plan"])
```
**Rückgabewert:** `UsageLimits`
**Antwortstruktur:**
```python
{
"success": bool,
"rateLimit": {
"sync": {
"isLimited": bool,
"limit": int,
"remaining": int,
"resetAt": str
},
"async": {
"isLimited": bool,
"limit": int,
"remaining": int,
"resetAt": str
},
"authType": str # 'api' or 'manual'
},
"usage": {
"currentPeriodCost": float,
"limit": float,
"plan": str # e.g., 'free', 'pro'
}
}
```
##### set_api_key()
Aktualisiert den API-Schlüssel.
```python
client.set_api_key("new-api-key")
```
##### set_base_url()
Aktualisiert die Basis-URL.
```python
client.set_base_url("https://my-custom-domain.com")
```
##### close()
Schließt die zugrunde liegende HTTP-Sitzung.
```python
client.close()
```
## Datenklassen
### WorkflowExecutionResult
```python
@dataclass
class WorkflowExecutionResult:
success: bool
output: Optional[Any] = None
error: Optional[str] = None
logs: Optional[List[Any]] = None
metadata: Optional[Dict[str, Any]] = None
trace_spans: Optional[List[Any]] = None
total_duration: Optional[float] = None
```
### AsyncExecutionResult
```python
@dataclass
class AsyncExecutionResult:
success: bool
task_id: str
status: str # 'queued'
created_at: str
links: Dict[str, str] # e.g., {"status": "/api/jobs/{taskId}"}
```
### WorkflowStatus
```python
@dataclass
class WorkflowStatus:
is_deployed: bool
deployed_at: Optional[str] = None
needs_redeployment: bool = False
```
### RateLimitInfo
```python
@dataclass
class RateLimitInfo:
limit: int
remaining: int
reset: int
retry_after: Optional[int] = None
```
### UsageLimits
```python
@dataclass
class UsageLimits:
success: bool
rate_limit: Dict[str, Any]
usage: Dict[str, Any]
```
### SimStudioError
```python
class SimStudioError(Exception):
def __init__(self, message: str, code: Optional[str] = None, status: Optional[int] = None):
super().__init__(message)
self.code = code
self.status = status
```
**Häufige Fehlercodes:**
- `UNAUTHORIZED`: Ungültiger API-Schlüssel
- `TIMEOUT`: Zeitüberschreitung der Anfrage
- `RATE_LIMIT_EXCEEDED`: Ratenlimit überschritten
- `USAGE_LIMIT_EXCEEDED`: Nutzungslimit überschritten
- `EXECUTION_ERROR`: Workflow-Ausführung fehlgeschlagen
## Beispiele
### Grundlegende Workflow-Ausführung
<Steps>
<Step title="Client initialisieren">
Richten Sie den SimStudioClient mit Ihrem API-Schlüssel ein.
</Step>
<Step title="Workflow validieren">
Prüfen Sie, ob der Workflow bereitgestellt und zur Ausführung bereit ist.
</Step>
<Step title="Workflow ausführen">
Führen Sie den Workflow mit Ihren Eingabedaten aus.
</Step>
<Step title="Ergebnis verarbeiten">
Verarbeiten Sie das Ausführungsergebnis und behandeln Sie eventuelle Fehler.
</Step>
</Steps>
```python
import os
from simstudio import SimStudioClient
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def run_workflow():
try:
# Check if workflow is ready
is_ready = client.validate_workflow("my-workflow-id")
if not is_ready:
raise Exception("Workflow is not deployed or ready")
# Execute the workflow
result = client.execute_workflow(
"my-workflow-id",
input_data={
"message": "Process this data",
"user_id": "12345"
}
)
if result.success:
print("Output:", result.output)
print("Duration:", result.metadata.get("duration") if result.metadata else None)
else:
print("Workflow failed:", result.error)
except Exception as error:
print("Error:", error)
run_workflow()
```
### Fehlerbehandlung
Behandeln Sie verschiedene Fehlertypen, die während der Workflow-Ausführung auftreten können:
```python
from simstudio import SimStudioClient, SimStudioError
import os
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_error_handling():
try:
result = client.execute_workflow("workflow-id")
return result
except SimStudioError as error:
if error.code == "UNAUTHORIZED":
print("Invalid API key")
elif error.code == "TIMEOUT":
print("Workflow execution timed out")
elif error.code == "USAGE_LIMIT_EXCEEDED":
print("Usage limit exceeded")
elif error.code == "INVALID_JSON":
print("Invalid JSON in request body")
else:
print(f"Workflow error: {error}")
raise
except Exception as error:
print(f"Unexpected error: {error}")
raise
```
### Verwendung des Context-Managers
Verwenden Sie den Client als Context-Manager, um die Ressourcenbereinigung automatisch zu handhaben:
```python
from simstudio import SimStudioClient
import os
# Using context manager to automatically close the session
with SimStudioClient(api_key=os.getenv("SIM_API_KEY")) as client:
result = client.execute_workflow("workflow-id")
print("Result:", result)
# Session is automatically closed here
```
### Batch-Workflow-Ausführung
Führen Sie mehrere Workflows effizient aus:
```python
from simstudio import SimStudioClient
import os
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_workflows_batch(workflow_data_pairs):
"""Execute multiple workflows with different input data."""
results = []
for workflow_id, input_data in workflow_data_pairs:
try:
# Validate workflow before execution
if not client.validate_workflow(workflow_id):
print(f"Skipping {workflow_id}: not deployed")
continue
result = client.execute_workflow(workflow_id, input_data)
results.append({
"workflow_id": workflow_id,
"success": result.success,
"output": result.output,
"error": result.error
})
except Exception as error:
results.append({
"workflow_id": workflow_id,
"success": False,
"error": str(error)
})
return results
# Example usage
workflows = [
("workflow-1", {"type": "analysis", "data": "sample1"}),
("workflow-2", {"type": "processing", "data": "sample2"}),
]
results = execute_workflows_batch(workflows)
for result in results:
print(f"Workflow {result['workflow_id']}: {'Success' if result['success'] else 'Failed'}")
```
### Asynchrone Workflow-Ausführung
Führen Sie Workflows asynchron für langwierige Aufgaben aus:
```python
import os
import time
from simstudio import SimStudioClient
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_async():
try:
# Start async execution
result = client.execute_workflow(
"workflow-id",
input_data={"data": "large dataset"},
async_execution=True # Execute asynchronously
)
# Check if result is an async execution
if hasattr(result, 'task_id'):
print(f"Task ID: {result.task_id}")
print(f"Status endpoint: {result.links['status']}")
# Poll for completion
status = client.get_job_status(result.task_id)
while status["status"] in ["queued", "processing"]:
print(f"Current status: {status['status']}")
time.sleep(2) # Wait 2 seconds
status = client.get_job_status(result.task_id)
if status["status"] == "completed":
print("Workflow completed!")
print(f"Output: {status['output']}")
print(f"Duration: {status['metadata']['duration']}")
else:
print(f"Workflow failed: {status['error']}")
except Exception as error:
print(f"Error: {error}")
execute_async()
```
### Ratenlimitierung und Wiederholung
Behandeln Sie Ratenbegrenzungen automatisch mit exponentiellem Backoff:
```python
import os
from simstudio import SimStudioClient, SimStudioError
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_retry_handling():
try:
# Automatically retries on rate limit
result = client.execute_with_retry(
"workflow-id",
input_data={"message": "Process this"},
max_retries=5,
initial_delay=1.0,
max_delay=60.0,
backoff_multiplier=2.0
)
print(f"Success: {result}")
except SimStudioError as error:
if error.code == "RATE_LIMIT_EXCEEDED":
print("Rate limit exceeded after all retries")
# Check rate limit info
rate_limit_info = client.get_rate_limit_info()
if rate_limit_info:
from datetime import datetime
reset_time = datetime.fromtimestamp(rate_limit_info.reset)
print(f"Rate limit resets at: {reset_time}")
execute_with_retry_handling()
```
### Nutzungsüberwachung
Überwachen Sie die Nutzung und Limits Ihres Kontos:
```python
import os
from simstudio import SimStudioClient
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def check_usage():
try:
limits = client.get_usage_limits()
print("=== Rate Limits ===")
print("Sync requests:")
print(f" Limit: {limits.rate_limit['sync']['limit']}")
print(f" Remaining: {limits.rate_limit['sync']['remaining']}")
print(f" Resets at: {limits.rate_limit['sync']['resetAt']}")
print(f" Is limited: {limits.rate_limit['sync']['isLimited']}")
print("\nAsync requests:")
print(f" Limit: {limits.rate_limit['async']['limit']}")
print(f" Remaining: {limits.rate_limit['async']['remaining']}")
print(f" Resets at: {limits.rate_limit['async']['resetAt']}")
print(f" Is limited: {limits.rate_limit['async']['isLimited']}")
print("\n=== Usage ===")
print(f"Current period cost: ${limits.usage['currentPeriodCost']:.2f}")
print(f"Limit: ${limits.usage['limit']:.2f}")
print(f"Plan: {limits.usage['plan']}")
percent_used = (limits.usage['currentPeriodCost'] / limits.usage['limit']) * 100
print(f"Usage: {percent_used:.1f}%")
if percent_used > 80:
print("⚠️ Warning: You are approaching your usage limit!")
except Exception as error:
print(f"Error checking usage: {error}")
check_usage()
```
### Streaming-Workflow-Ausführung
Führen Sie Workflows mit Echtzeit-Streaming-Antworten aus:
```python
from simstudio import SimStudioClient
import os
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_streaming():
"""Execute workflow with streaming enabled."""
try:
# Enable streaming for specific block outputs
result = client.execute_workflow(
"workflow-id",
input_data={"message": "Count to five"},
stream=True,
selected_outputs=["agent1.content"] # Use blockName.attribute format
)
print("Workflow result:", result)
except Exception as error:
print("Error:", error)
execute_with_streaming()
```
Die Streaming-Antwort folgt dem Server-Sent-Events- (SSE-) Format:
```
data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":"One"}
data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":", two"}
data: {"event":"done","success":true,"output":{},"metadata":{"duration":610}}
data: [DONE]
```
**Flask-Streaming-Beispiel:**
```python
from flask import Flask, Response, stream_with_context
import requests
import json
import os
app = Flask(__name__)
@app.route('/stream-workflow')
def stream_workflow():
"""Stream workflow execution to the client."""
def generate():
response = requests.post(
'https://sim.ai/api/workflows/WORKFLOW_ID/execute',
headers={
'Content-Type': 'application/json',
'X-API-Key': os.getenv('SIM_API_KEY')
},
json={
'message': 'Generate a story',
'stream': True,
'selectedOutputs': ['agent1.content']
},
stream=True
)
for line in response.iter_lines():
if line:
decoded_line = line.decode('utf-8')
if decoded_line.startswith('data: '):
data = decoded_line[6:] # Remove 'data: ' prefix
if data == '[DONE]':
break
try:
parsed = json.loads(data)
if 'chunk' in parsed:
yield f"data: {json.dumps(parsed)}\n\n"
elif parsed.get('event') == 'done':
yield f"data: {json.dumps(parsed)}\n\n"
print("Execution complete:", parsed.get('metadata'))
except json.JSONDecodeError:
pass
return Response(
stream_with_context(generate()),
mimetype='text/event-stream'
)
if __name__ == '__main__':
app.run(debug=True)
```
### Umgebungs­konfiguration
Konfigurieren Sie den Client mit Umgebungsvariablen:
<Tabs items={['Development', 'Production']}>
<Tab value="Development">
```python
import os
from simstudio import SimStudioClient
# Development configuration
client = SimStudioClient(
api_key=os.getenv("SIM_API_KEY")
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```
</Tab>
<Tab value="Production">
```python
import os
from simstudio import SimStudioClient
# Production configuration with error handling
api_key = os.getenv("SIM_API_KEY")
if not api_key:
raise ValueError("SIM_API_KEY environment variable is required")
client = SimStudioClient(
api_key=api_key,
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```
</Tab>
</Tabs>
## Ihren API-Schlüssel erhalten
<Steps>
<Step title="Bei Sim anmelden">
Navigieren Sie zu [Sim](https://sim.ai) und melden Sie sich in Ihrem Konto an.
</Step>
<Step title="Workflow öffnen">
Navigieren Sie zu dem Workflow, den Sie programmatisch ausführen möchten.
</Step>
<Step title="Workflow bereitstellen">
Klicken Sie auf "Bereitstellen", um Ihren Workflow bereitzustellen, falls dies noch nicht geschehen ist.
</Step>
<Step title="API-Schlüssel erstellen oder auswählen">
Wählen oder erstellen Sie während des Bereitstellungsprozesses einen API-Schlüssel.
</Step>
<Step title="API-Schlüssel kopieren">
Kopieren Sie den API-Schlüssel, um ihn in Ihrer Python-Anwendung zu verwenden.
</Step>
</Steps>
## Voraussetzungen
- Python 3.8+
- requests >= 2.25.0
## Lizenz
Apache-2.0

File diff suppressed because it is too large Load Diff

View File

@@ -152,3 +152,9 @@ Input → Agent (Google Search, Notion) → Function (Compile Report)
- **Sei spezifisch in System-Prompts**: Definiere die Rolle, den Ton und die Einschränkungen des Agenten klar. Je spezifischer deine Anweisungen sind, desto besser kann der Agent seinen vorgesehenen Zweck erfüllen.
- **Wähle die richtige Temperatureinstellung**: Verwende niedrigere Temperatureinstellungen (0-0,3), wenn Genauigkeit wichtig ist, oder erhöhe die Temperatur (0,7-2,0) für kreativere oder vielfältigere Antworten
- **Nutze Tools effektiv**: Integriere Tools, die den Zweck des Agenten ergänzen und seine Fähigkeiten erweitern. Sei selektiv bei der Auswahl der Tools, um den Agenten nicht zu überfordern. Für Aufgaben mit wenig Überschneidung verwende einen anderen Agent-Block für die besten Ergebnisse.
## Best Practices
- **Seien Sie spezifisch in System-Prompts**: Definieren Sie die Rolle, den Ton und die Grenzen des Agenten klar. Je spezifischer Ihre Anweisungen sind, desto besser kann der Agent seinen beabsichtigten Zweck erfüllen.
- **Wählen Sie die richtige Temperatureinstellung**: Verwenden Sie niedrigere Temperatureinstellungen (00,3), wenn Genauigkeit wichtig ist, oder erhöhen Sie die Temperatur (0,72,0) für kreativere oder vielfältigere Antworten
- **Nutzen Sie Tools effektiv**: Integrieren Sie Tools, die den Zweck des Agenten ergänzen und seine Fähigkeiten erweitern. Seien Sie selektiv bei der Auswahl der Tools, um den Agenten nicht zu überfordern. Verwenden Sie für Aufgaben mit geringer Überschneidung einen weiteren Agent-Block für die besten Ergebnisse.

View File

@@ -190,13 +190,8 @@ console.log(`${processedItems} gültige Elemente verarbeitet`);
### Einschränkungen
<Callout type="warning">
Container-Blöcke (Schleifen und Parallele) können nicht ineinander verschachtelt werden. Das bedeutet:
- Du kannst keinen Schleifenblock in einen anderen Schleifenblock platzieren
- Du kannst keinen Parallel-Block in einen Schleifenblock platzieren
- Du kannst keinen Container-Block in einen anderen Container-Block platzieren
Wenn du mehrdimensionale Iterationen benötigst, erwäge eine Umstrukturierung deines Workflows, um sequentielle Schleifen zu verwenden oder Daten in Stufen zu verarbeiten.
<Callout type="info">
Container-Blöcke (Schleifen und Parallele) unterstützen Verschachtelung. Du kannst Schleifen in Schleifen, Parallele in Schleifen und jede Kombination von Container-Blöcken platzieren, um komplexe mehrdimensionale Workflows zu erstellen.
</Callout>
<Callout type="info">
@@ -255,3 +250,57 @@ console.log(`${processedItems} gültige Elemente verarbeitet`);
- **Setzen Sie vernünftige Grenzen**: Halten Sie die Anzahl der Iterationen in einem vernünftigen Rahmen, um lange Ausführungszeiten zu vermeiden
- **Verwenden Sie ForEach für Sammlungen**: Verwenden Sie beim Verarbeiten von Arrays oder Objekten ForEach anstelle von For-Schleifen
- **Behandeln Sie Fehler elegant**: Erwägen Sie, Fehlerbehandlung innerhalb von Schleifen hinzuzufügen, um robuste Workflows zu gewährleisten
## Eingaben und Ausgaben
<Tabs items={['Configuration', 'Variables', 'Results']}>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>Schleifentyp</strong>: Wählen Sie zwischen 'for', 'forEach', 'while' oder 'doWhile'
</li>
<li>
<strong>Iterationen</strong>: Anzahl der Ausführungen (für for-Schleifen)
</li>
<li>
<strong>Sammlung</strong>: Array oder Objekt zum Durchlaufen (für forEach-Schleifen)
</li>
<li>
<strong>Bedingung</strong>: Boolescher Ausdruck zur Auswertung (für while/do-while-Schleifen)
</li>
</ul>
</Tab>
<Tab>
Verfügbar **innerhalb** der Schleife:
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<loop.index>"}</strong>: Aktuelle Iterationsnummer (0-basiert)
</li>
<li>
<strong>{"<loop.currentItem>"}</strong>: Aktuell verarbeitetes Element (nur forEach)
</li>
<li>
<strong>{"<loop.items>"}</strong>: Vollständige Sammlung (nur forEach)
</li>
</ul>
</Tab>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<blockname.results>"}</strong>: Array aller Iterationsergebnisse (Zugriff über Blocknamen)
</li>
<li>
<strong>Struktur</strong>: Ergebnisse behalten die Iterationsreihenfolge bei
</li>
<li>
<strong>Zugriff</strong>: Verfügbar in Blöcken nach Abschluss der Schleife
</li>
</ul>
</Tab>
</Tabs>
## Best Practices
- **Setzen Sie vernünftige Grenzen**: Halten Sie die Iterationsanzahl angemessen, um lange Ausführungszeiten zu vermeiden
- **Verwenden Sie ForEach für Sammlungen**: Verwenden Sie beim Verarbeiten von Arrays oder Objekten ForEach anstelle von For-Schleifen
- **Behandeln Sie Fehler elegant**: Erwägen Sie, Fehlerbehandlung innerhalb von Schleifen hinzuzufügen, um robuste Workflows zu gewährleisten

View File

@@ -142,11 +142,8 @@ Jede parallele Instanz läuft unabhängig:
### Einschränkungen
<Callout type="warning">
Container-Blöcke (Schleifen und Parallele) können nicht ineinander verschachtelt werden. Das bedeutet:
- Sie können keinen Schleifenblock in einen Parallelblock platzieren
- Sie können keinen weiteren Parallelblock in einen Parallelblock platzieren
- Sie können keinen Container-Block in einen anderen Container-Block platzieren
<Callout type="info">
Container-Blöcke (Schleifen und Parallele) unterstützen Verschachtelung. Sie können Parallele in Parallele, Schleifen in Parallele und jede Kombination von Container-Blöcken platzieren, um komplexe mehrdimensionale Workflows zu erstellen.
</Callout>
<Callout type="info">
@@ -214,3 +211,51 @@ Wann Sie welche Methode verwenden sollten:
- **Nur unabhängige Operationen**: Stellen Sie sicher, dass Operationen nicht voneinander abhängen
- **Ratenbegrenzungen berücksichtigen**: Fügen Sie Verzögerungen oder Drosselungen für API-intensive Workflows hinzu
- **Fehlerbehandlung**: Jede Instanz sollte ihre eigenen Fehler angemessen behandeln
## Eingaben und Ausgaben
<Tabs items={['Konfiguration', 'Variablen', 'Ergebnisse']}>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>Parallel-Typ</strong>: Wählen Sie zwischen „count" oder „collection"
</li>
<li>
<strong>Anzahl</strong>: Anzahl der auszuführenden Instanzen (anzahlbasiert)
</li>
<li>
<strong>Collection</strong>: Array oder Objekt zur Verteilung (sammlungsbasiert)
</li>
</ul>
</Tab>
<Tab>
Verfügbar **innerhalb** der Parallelverarbeitung:
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<parallel.index>"}</strong>: Instanznummer (0-basiert)
</li>
<li>
<strong>{"<parallel.currentItem>"}</strong>: Element für diese Instanz (nur sammlungsbasiert)
</li>
<li>
<strong>{"<parallel.items>"}</strong>: Vollständige Sammlung (nur sammlungsbasiert)
</li>
</ul>
</Tab>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<blockname.results>"}</strong>: Array aller Instanzergebnisse (Zugriff über Blockname)
</li>
<li>
<strong>Zugriff</strong>: Verfügbar in Blöcken nach Abschluss der Parallelverarbeitung
</li>
</ul>
</Tab>
</Tabs>
## Best Practices
- **Nur unabhängige Operationen**: Stellen Sie sicher, dass Operationen nicht voneinander abhängen
- **Rate Limits beachten**: Fügen Sie Verzögerungen oder Drosselung für API-intensive Workflows hinzu
- **Fehlerbehandlung**: Jede Instanz sollte ihre eigenen Fehler ordnungsgemäß behandeln

View File

@@ -100,3 +100,18 @@ Input (Lead) → Router → Agent (Enterprise Sales) or Workflow (Self-serve)
- **Mit verschiedenen Eingaben testen**: Stellen Sie sicher, dass der Router verschiedene Eingabetypen, Grenzfälle und unerwartete Inhalte verarbeiten kann
- **Routing-Leistung überwachen**: Überprüfen Sie Routing-Entscheidungen regelmäßig und verfeinern Sie Kriterien basierend auf tatsächlichen Nutzungsmustern
- **Geeignete Modelle auswählen**: Verwenden Sie Modelle mit starken Argumentationsfähigkeiten für komplexe Routing-Entscheidungen
Wenn der Router keine geeignete Route für den gegebenen Kontext ermitteln kann, leitet er stattdessen zum **Fehlerpfad** weiter, anstatt willkürlich eine Route auszuwählen. Dies geschieht, wenn:
- Der Kontext keiner der definierten Routenbeschreibungen eindeutig entspricht
- Die KI feststellt, dass keine der verfügbaren Routen geeignet ist
## Best Practices
- **Klare Routenbeschreibungen verfassen**: Jede Routenbeschreibung sollte klar erklären, wann diese Route ausgewählt werden sollte. Seien Sie spezifisch bezüglich der Kriterien.
- **Routen gegenseitig ausschließend gestalten**: Stellen Sie nach Möglichkeit sicher, dass sich Routenbeschreibungen nicht überschneiden, um mehrdeutige Routing-Entscheidungen zu vermeiden.
- **Einen Fehlerpfad verbinden**: Behandeln Sie Fälle, in denen keine Route passt, indem Sie einen Fehlerbehandler für ein elegantes Fallback-Verhalten verbinden.
- **Aussagekräftige Routentitel verwenden**: Routentitel erscheinen im Workflow-Canvas, machen Sie sie daher für bessere Lesbarkeit aussagekräftig.
- **Mit verschiedenen Eingaben testen**: Stellen Sie sicher, dass der Router verschiedene Eingabetypen, Grenzfälle und unerwartete Inhalte verarbeitet.
- **Routing-Performance überwachen**: Überprüfen Sie Routing-Entscheidungen regelmäßig und verfeinern Sie Routenbeschreibungen basierend auf tatsächlichen Nutzungsmustern.
- **Geeignete Modelle wählen**: Verwenden Sie Modelle mit starken Reasoning-Fähigkeiten für komplexe Routing-Entscheidungen.

View File

@@ -0,0 +1,89 @@
---
title: Webhook
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
Der Webhook-Block sendet HTTP-POST-Anfragen an externe Webhook-Endpunkte mit automatischen Webhook-Headern und optionaler HMAC-Signierung.
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Webhook-Block"
width={500}
height={400}
className="my-6"
/>
</div>
## Konfiguration
### Webhook-URL
Der Ziel-Endpunkt für Ihre Webhook-Anfrage. Unterstützt sowohl statische URLs als auch dynamische Werte aus anderen Blöcken.
### Payload
JSON-Daten, die im Anfrage-Body gesendet werden. Verwenden Sie den KI-Zauberstab, um Payloads zu generieren oder auf Workflow-Variablen zu verweisen:
```json
{
"event": "workflow.completed",
"data": {
"result": "<agent.content>",
"timestamp": "<function.result>"
}
}
```
### Signierungsgeheimnis
Optionales Geheimnis für die HMAC-SHA256-Payload-Signierung. Wenn angegeben, wird ein `X-Webhook-Signature`Header hinzugefügt:
```
X-Webhook-Signature: t=1704067200000,v1=5d41402abc4b2a76b9719d911017c592...
```
Um Signaturen zu verifizieren, berechnen Sie `HMAC-SHA256(secret, "${timestamp}.${body}")` und vergleichen Sie mit dem `v1`Wert.
### Zusätzliche Header
Benutzerdefinierte Schlüssel-Wert-Header, die in die Anfrage aufgenommen werden. Diese überschreiben alle automatischen Header mit demselben Namen.
## Automatische Header
Jede Anfrage enthält automatisch diese Header:
| Header | Beschreibung |
|--------|-------------|
| `Content-Type` | `application/json` |
| `X-Webhook-Timestamp` | Unix-Zeitstempel in Millisekunden |
| `X-Delivery-ID` | Eindeutige UUID für diese Zustellung |
| `Idempotency-Key` | Identisch mit `X-Delivery-ID` zur Deduplizierung |
## Ausgaben
| Ausgabe | Typ | Beschreibung |
|--------|------|-------------|
| `data` | json | Antwort-Body vom Endpunkt |
| `status` | number | HTTP-Statuscode |
| `headers` | object | Antwort-Header |
## Beispiel-Anwendungsfälle
**Externe Dienste benachrichtigen** - Workflow-Ergebnisse an Slack, Discord oder benutzerdefinierte Endpunkte senden
```
Agent → Function (format) → Webhook (notify)
```
**Externe Workflows auslösen** - Prozesse in anderen Systemen starten, wenn Bedingungen erfüllt sind
```
Condition (check) → Webhook (trigger) → Response
```
<Callout>
Der Webhook-Block verwendet immer POST. Für andere HTTP-Methoden oder mehr Kontrolle verwenden Sie den [API-Block](/blocks/api).
</Callout>

View File

@@ -169,3 +169,175 @@ copilotCost = (inputTokens × inputPrice + outputTokens × (outputPrice × 1.5))
<Callout type="info">
Modellpreise werden pro Million Tokens angegeben. Die Berechnung teilt durch 1.000.000, um die tatsächlichen Kosten zu ermitteln. Siehe <a href="/execution/costs">die Seite zur Kostenberechnung</a> für Hintergründe und Beispiele.
</Callout>
Fahre mit der Maus über eine deiner Nachrichten und klicke auf **Bearbeiten**, um sie zu ändern und erneut zu senden. Dies ist nützlich, um deine Eingaben zu verfeinern.
### Nachrichtenwarteschlange
Wenn du eine Nachricht sendest, während Copilot noch antwortet, wird sie in die Warteschlange gestellt. Du kannst:
- Warteschlangennachrichten im erweiterbaren Warteschlangenpanel anzeigen
- Eine Nachricht aus der Warteschlange sofort senden (bricht die aktuelle Antwort ab)
- Nachrichten aus der Warteschlange entfernen
## Dateianhänge
Klicke auf das Anhang-Symbol, um Dateien mit deiner Nachricht hochzuladen. Unterstützte Dateitypen umfassen:
- Bilder (Vorschau-Thumbnails werden angezeigt)
- PDFs
- Textdateien, JSON, XML
- Andere Dokumentformate
Dateien werden als anklickbare Thumbnails angezeigt, die in einem neuen Tab geöffnet werden.
## Checkpoints & Änderungen
Wenn Copilot Änderungen an deinem Workflow vornimmt, speichert es Checkpoints, damit du bei Bedarf zurückkehren kannst.
### Checkpoints anzeigen
Fahre mit der Maus über eine Copilot-Nachricht und klicke auf das Checkpoints-Symbol, um gespeicherte Workflow-Zustände für diese Nachricht anzuzeigen.
### Änderungen rückgängig machen
Klicke bei jedem Checkpoint auf **Rückgängig machen**, um deinen Workflow auf diesen Zustand zurückzusetzen. Ein Bestätigungsdialog warnt dich, dass diese Aktion nicht rückgängig gemacht werden kann.
### Änderungen akzeptieren
Wenn Copilot Änderungen vorschlägt, kannst du:
- **Akzeptieren**: Die vorgeschlagenen Änderungen anwenden (`Mod+Shift+Enter`)
- **Ablehnen**: Die Änderungen verwerfen und deinen aktuellen Workflow beibehalten
## Denkblöcke
Bei komplexen Anfragen kann Copilot seinen Denkprozess in erweiterbaren Denkblöcken anzeigen:
- Blöcke werden automatisch erweitert, während Copilot denkt
- Klicken zum manuellen Erweitern/Reduzieren
- Zeigt die Dauer des Denkprozesses an
- Hilft dir zu verstehen, wie Copilot zu seiner Lösung gekommen ist
## Optionsauswahl
Wenn Copilot mehrere Optionen präsentiert, kannst du auswählen mit:
| Steuerung | Aktion |
|---------|--------|
| **1-9** | Option nach Nummer auswählen |
| **Pfeiltaste auf/ab** | Zwischen Optionen navigieren |
| **Eingabetaste** | Hervorgehobene Option auswählen |
Ausgewählte Optionen sind hervorgehoben; nicht ausgewählte Optionen erscheinen durchgestrichen.
## Tastenkombinationen
| Tastenkombination | Aktion |
|----------|--------|
| `@` | Kontextmenü öffnen |
| `/` | Slash-Befehle öffnen |
| `Arrow Up/Down` | Menüelemente navigieren |
| `Enter` | Menüelement auswählen |
| `Esc` | Menüs schließen |
| `Mod+Shift+Enter` | Copilot-Änderungen akzeptieren |
## Nutzungslimits
Die Copilot-Nutzung wird pro Token des zugrunde liegenden LLM abgerechnet. Wenn Sie Ihr Nutzungslimit erreichen, fordert Copilot Sie auf, Ihr Limit zu erhöhen. Sie können die Nutzung in Schritten (50 $, 100 $) von Ihrer aktuellen Basis aus hinzufügen.
<Callout type="info">
Siehe die [Seite zur Kostenberechnung](/execution/costs) für Abrechnungsdetails.
</Callout>
## Copilot MCP
Sie können Copilot als MCP-Server in Ihrem bevorzugten Editor oder AI-Client verwenden. Damit können Sie Sim-Workflows direkt aus Tools wie Cursor, Claude Code, Claude Desktop und VS Code erstellen, testen, bereitstellen und verwalten.
### Generieren eines Copilot-API-Schlüssels
Um sich mit dem Copilot-MCP-Server zu verbinden, benötigen Sie einen **Copilot-API-Schlüssel**:
1. Gehen Sie zu [sim.ai](https://sim.ai) und melden Sie sich an
2. Navigieren Sie zu **Einstellungen** → **Copilot**
3. Klicken Sie auf **API-Schlüssel generieren**
4. Kopieren Sie den Schlüssel er wird nur einmal angezeigt
Der Schlüssel sieht aus wie `sk-sim-copilot-...`. Sie werden ihn in der folgenden Konfiguration verwenden.
### Cursor
Fügen Sie Folgendes zu Ihrer `.cursor/mcp.json` (Projektebene) oder den globalen Cursor-MCP-Einstellungen hinzu:
```json
{
"mcpServers": {
"sim-copilot": {
"url": "https://www.sim.ai/api/mcp/copilot",
"headers": {
"X-API-Key": "YOUR_COPILOT_API_KEY"
}
}
}
}
```
Ersetzen Sie `YOUR_COPILOT_API_KEY` durch den oben generierten Schlüssel.
### Claude Code
Führen Sie den folgenden Befehl aus, um den Copilot MCP-Server hinzuzufügen:
```bash
claude mcp add sim-copilot \
--transport http \
https://www.sim.ai/api/mcp/copilot \
--header "X-API-Key: YOUR_COPILOT_API_KEY"
```
Ersetzen Sie `YOUR_COPILOT_API_KEY` durch Ihren Schlüssel.
### Claude Desktop
Claude Desktop benötigt [`mcp-remote`](https://www.npmjs.com/package/mcp-remote), um sich mit HTTP-basierten MCP-Servern zu verbinden. Fügen Sie Folgendes zu Ihrer Claude Desktop-Konfigurationsdatei hinzu (`~/Library/Application Support/Claude/claude_desktop_config.json` unter macOS):
```json
{
"mcpServers": {
"sim-copilot": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://www.sim.ai/api/mcp/copilot",
"--header",
"X-API-Key: YOUR_COPILOT_API_KEY"
]
}
}
}
```
Ersetzen Sie `YOUR_COPILOT_API_KEY` durch Ihren Schlüssel.
### VS Code
Fügen Sie Folgendes zu Ihrer VS Code `settings.json` oder Workspace `.vscode/settings.json` hinzu:
```json
{
"mcp": {
"servers": {
"sim-copilot": {
"type": "http",
"url": "https://www.sim.ai/api/mcp/copilot",
"headers": {
"X-API-Key": "YOUR_COPILOT_API_KEY"
}
}
}
}
}
```
Ersetzen Sie `YOUR_COPILOT_API_KEY` durch Ihren Schlüssel.
<Callout type="info">
Für selbst gehostete Deployments ersetzen Sie `https://www.sim.ai` durch Ihre selbst gehostete Sim-URL.
</Callout>

View File

@@ -0,0 +1,114 @@
---
title: Enterprise
description: Enterprise-Funktionen für Organisationen mit erweiterten
Sicherheits- und Compliance-Anforderungen
---
import { Callout } from 'fumadocs-ui/components/callout'
Sim Enterprise bietet erweiterte Funktionen für Organisationen mit erhöhten Sicherheits-, Compliance- und Verwaltungsanforderungen.
---
## Bring Your Own Key (BYOK)
Verwenden Sie Ihre eigenen API-Schlüssel für KI-Modellanbieter anstelle der gehosteten Schlüssel von Sim.
### Unterstützte Anbieter
| Anbieter | Verwendung |
|----------|-------|
| OpenAI | Knowledge Base-Embeddings, Agent-Block |
| Anthropic | Agent-Block |
| Google | Agent-Block |
| Mistral | Knowledge Base OCR |
### Einrichtung
1. Navigieren Sie zu **Einstellungen** → **BYOK** in Ihrem Workspace
2. Klicken Sie auf **Schlüssel hinzufügen** für Ihren Anbieter
3. Geben Sie Ihren API-Schlüssel ein und speichern Sie
<Callout type="warn">
BYOK-Schlüssel werden verschlüsselt gespeichert. Nur Organisationsadministratoren und -inhaber können Schlüssel verwalten.
</Callout>
Wenn konfiguriert, verwenden Workflows Ihren Schlüssel anstelle der gehosteten Schlüssel von Sim. Bei Entfernung wechseln Workflows automatisch zu den gehosteten Schlüsseln zurück.
---
## Single Sign-On (SSO)
Enterprise-Authentifizierung mit SAML 2.0- und OIDC-Unterstützung für zentralisiertes Identitätsmanagement.
### Unterstützte Anbieter
- Okta
- Azure AD / Entra ID
- Google Workspace
- OneLogin
- Jeder SAML 2.0- oder OIDC-Anbieter
### Einrichtung
1. Navigieren Sie zu **Einstellungen** → **SSO** in Ihrem Workspace
2. Wählen Sie Ihren Identitätsanbieter
3. Konfigurieren Sie die Verbindung mithilfe der Metadaten Ihres IdP
4. Aktivieren Sie SSO für Ihre Organisation
<Callout type="info">
Sobald SSO aktiviert ist, authentifizieren sich Teammitglieder über Ihren Identitätsanbieter anstelle von E-Mail/Passwort.
</Callout>
---
## Self-Hosted
Für selbst gehostete Bereitstellungen können Enterprise-Funktionen über Umgebungsvariablen aktiviert werden:
| Variable | Beschreibung |
|----------|-------------|
| `SSO_ENABLED`, `NEXT_PUBLIC_SSO_ENABLED` | Single Sign-On mit SAML/OIDC |
| `CREDENTIAL_SETS_ENABLED`, `NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED` | Polling-Gruppen für E-Mail-Trigger |
| `DISABLE_INVITATIONS`, `NEXT_PUBLIC_DISABLE_INVITATIONS` | Workspace-/Organisations-Einladungen global deaktivieren |
<Callout type="warn">
BYOK ist nur im gehosteten Sim verfügbar. Selbst gehostete Deployments konfigurieren AI-Provider-Schlüssel direkt über Umgebungsvariablen.
</Callout>
Wenn die Abrechnung deaktiviert ist, verwenden Sie die Admin-API zur Verwaltung von Organisationen:
```bash
# Create an organization
curl -X POST https://your-instance/api/v1/admin/organizations \
-H "x-admin-key: YOUR_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "My Organization", "ownerId": "user-id-here"}'
# Add a member
curl -X POST https://your-instance/api/v1/admin/organizations/{orgId}/members \
-H "x-admin-key: YOUR_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"userId": "user-id-here", "role": "admin"}'
```
### Workspace-Mitglieder
Wenn Einladungen deaktiviert sind, verwenden Sie die Admin-API zur direkten Verwaltung von Workspace-Mitgliedschaften:
```bash
# Add a user to a workspace
curl -X POST https://your-instance/api/v1/admin/workspaces/{workspaceId}/members \
-H "x-admin-key: YOUR_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"userId": "user-id-here", "permissions": "write"}'
# Remove a user from a workspace
curl -X DELETE "https://your-instance/api/v1/admin/workspaces/{workspaceId}/members?userId=user-id-here" \
-H "x-admin-key: YOUR_ADMIN_API_KEY"
```
### Hinweise
- Die Aktivierung von `ACCESS_CONTROL_ENABLED` aktiviert automatisch Organisationen, da die Zugriffskontrolle eine Organisationsmitgliedschaft erfordert.
- Wenn `DISABLE_INVITATIONS` gesetzt ist, können Benutzer keine Einladungen versenden. Verwenden Sie stattdessen die Admin-API zur Verwaltung von Workspace- und Organisationsmitgliedschaften.

View File

@@ -49,40 +49,40 @@ Die Modellaufschlüsselung zeigt:
<Tabs items={['Hosted Models', 'Bring Your Own API Key']}>
<Tab>
**Gehostete Modelle** - Sim stellt API-Schlüssel mit einem 2,5-fachen Preismultiplikator bereit:
**Hosted Models** - Sim bietet API-Schlüssel mit einem 1,4-fachen Preismultiplikator für Agent-Blöcke:
**OpenAI**
| Modell | Basispreis (Eingabe/Ausgabe) | Gehosteter Preis (Eingabe/Ausgabe) |
| Modell | Basispreis (Eingabe/Ausgabe) | Hosted-Preis (Eingabe/Ausgabe) |
|-------|---------------------------|----------------------------|
| GPT-5.1 | $1,25 / $10,00 | $3,13 / $25,00 |
| GPT-5 | $1,25 / $10,00 | $3,13 / $25,00 |
| GPT-5 Mini | $0,25 / $2,00 | $0,63 / $5,00 |
| GPT-5 Nano | $0,05 / $0,40 | $0,13 / $1,00 |
| GPT-4o | $2,50 / $10,00 | $6,25 / $25,00 |
| GPT-4.1 | $2,00 / $8,00 | $5,00 / $20,00 |
| GPT-4.1 Mini | $0,40 / $1,60 | $1,00 / $4,00 |
| GPT-4.1 Nano | $0,10 / $0,40 | $0,25 / $1,00 |
| o1 | $15,00 / $60,00 | $37,50 / $150,00 |
| o3 | $2,00 / $8,00 | $5,00 / $20,00 |
| o4 Mini | $1,10 / $4,40 | $2,75 / $11,00 |
| GPT-5.1 | $1.25 / $10.00 | $1.75 / $14.00 |
| GPT-5 | $1.25 / $10.00 | $1.75 / $14.00 |
| GPT-5 Mini | $0.25 / $2.00 | $0.35 / $2.80 |
| GPT-5 Nano | $0.05 / $0.40 | $0.07 / $0.56 |
| GPT-4o | $2.50 / $10.00 | $3.50 / $14.00 |
| GPT-4.1 | $2.00 / $8.00 | $2.80 / $11.20 |
| GPT-4.1 Mini | $0.40 / $1.60 | $0.56 / $2.24 |
| GPT-4.1 Nano | $0.10 / $0.40 | $0.14 / $0.56 |
| o1 | $15.00 / $60.00 | $21.00 / $84.00 |
| o3 | $2.00 / $8.00 | $2.80 / $11.20 |
| o4 Mini | $1.10 / $4.40 | $1.54 / $6.16 |
**Anthropic**
| Modell | Basispreis (Eingabe/Ausgabe) | Gehosteter Preis (Eingabe/Ausgabe) |
| Modell | Basispreis (Eingabe/Ausgabe) | Hosted-Preis (Eingabe/Ausgabe) |
|-------|---------------------------|----------------------------|
| Claude Opus 4.5 | $5,00 / $25,00 | $12,50 / $62,50 |
| Claude Opus 4.1 | $15,00 / $75,00 | $37,50 / $187,50 |
| Claude Sonnet 4.5 | $3,00 / $15,00 | $7,50 / $37,50 |
| Claude Sonnet 4.0 | $3,00 / $15,00 | $7,50 / $37,50 |
| Claude Haiku 4.5 | $1,00 / $5,00 | $2,50 / $12,50 |
| Claude Opus 4.5 | $5.00 / $25.00 | $7.00 / $35.00 |
| Claude Opus 4.1 | $15.00 / $75.00 | $21.00 / $105.00 |
| Claude Sonnet 4.5 | $3.00 / $15.00 | $4.20 / $21.00 |
| Claude Sonnet 4.0 | $3.00 / $15.00 | $4.20 / $21.00 |
| Claude Haiku 4.5 | $1.00 / $5.00 | $1.40 / $7.00 |
**Google**
| Modell | Basispreis (Eingabe/Ausgabe) | Gehosteter Preis (Eingabe/Ausgabe) |
| Modell | Basispreis (Eingabe/Ausgabe) | Hosted-Preis (Eingabe/Ausgabe) |
|-------|---------------------------|----------------------------|
| Gemini 3 Pro Preview | $2,00 / $12,00 | $5,00 / $30,00 |
| Gemini 2.5 Pro | $0,15 / $0,60 | $0,38 / $1,50 |
| Gemini 2.5 Flash | $0,15 / $0,60 | $0,38 / $1,50 |
| Gemini 3 Pro Preview | $2.00 / $12.00 | $2.80 / $16.80 |
| Gemini 2.5 Pro | $1.25 / $10.00 | $1.75 / $14.00 |
| Gemini 2.5 Flash | $0.30 / $2.50 | $0.42 / $3.50 |
*Der 2,5-fache Multiplikator deckt Infrastruktur- und API-Verwaltungskosten ab.*
*Der 1,4-fache Multiplikator deckt Infrastruktur- und API-Verwaltungskosten ab.*
</Tab>
<Tab>
@@ -105,28 +105,32 @@ Die Modellaufschlüsselung zeigt:
Die angezeigten Preise entsprechen den Tarifen vom 10. September 2025. Überprüfen Sie die Dokumentation der Anbieter für aktuelle Preise.
</Callout>
## Bring Your Own Key (BYOK)
Sie können Ihre eigenen API-Schlüssel für gehostete Modelle (OpenAI, Anthropic, Google, Mistral) unter **Einstellungen → BYOK** verwenden, um Basispreise zu zahlen. Schlüssel werden verschlüsselt und gelten arbeitsbereichsweit.
## Strategien zur Kostenoptimierung
- **Modellauswahl**: Wählen Sie Modelle basierend auf der Komplexität der Aufgabe. Einfache Aufgaben können GPT-4.1-nano verwenden, während komplexes Denken möglicherweise o1 oder Claude Opus erfordert.
- **Prompt-Engineering**: Gut strukturierte, präzise Prompts reduzieren den Token-Verbrauch ohne Qualitätseinbußen.
- **Modellauswahl**: Wählen Sie Modelle basierend auf der Aufgabenkomplexität. Einfache Aufgaben können GPT-4.1-nano verwenden, während komplexes Reasoning o1 oder Claude Opus erfordern könnte.
- **Prompt Engineering**: Gut strukturierte, prägnante Prompts reduzieren den Token-Verbrauch ohne Qualitätsverlust.
- **Lokale Modelle**: Verwenden Sie Ollama oder VLLM für unkritische Aufgaben, um API-Kosten vollständig zu eliminieren.
- **Caching und Wiederverwendung**: Speichern Sie häufig verwendete Ergebnisse in Variablen oder Dateien, um wiederholte KI-Modellaufrufe zu vermeiden.
- **Batch-Verarbeitung**: Verarbeiten Sie mehrere Elemente in einer einzigen KI-Anfrage anstatt einzelne Aufrufe zu tätigen.
- **Caching und Wiederverwendung**: Speichern Sie häufig verwendete Ergebnisse in Variablen oder Dateien, um wiederholte AI-Modellaufrufe zu vermeiden.
- **Batch-Verarbeitung**: Verarbeiten Sie mehrere Elemente in einer einzigen AI-Anfrage, anstatt einzelne Aufrufe zu tätigen.
## Nutzungsüberwachung
Überwachen Sie Ihre Nutzung und Abrechnung unter Einstellungen → Abonnement:
- **Aktuelle Nutzung**: Echtzeit-Nutzung und -Kosten für den aktuellen Zeitraum
- **Nutzungslimits**: Plangrenzen mit visuellen Fortschrittsanzeigen
- **Aktuelle Nutzung**: Echtzeit-Nutzung und Kosten für den aktuellen Zeitraum
- **Nutzungslimits**: Plan-Limits mit visuellen Fortschrittsindikatoren
- **Abrechnungsdetails**: Prognostizierte Gebühren und Mindestverpflichtungen
- **Planverwaltung**: Upgrade-Optionen und Abrechnungsverlauf
- **Plan-Verwaltung**: Upgrade-Optionen und Abrechnungsverlauf
### Programmatische Nutzungsverfolgung
### Programmatisches Nutzungs-Tracking
Sie können Ihre aktuelle Nutzung und Limits programmatisch über die API abfragen:
**Endpunkt:**
**Endpoint:**
```text
GET /api/users/me/usage-limits
@@ -172,69 +176,110 @@ curl -X GET -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" htt
```
**Rate-Limit-Felder:**
- `requestsPerMinute`: Dauerhafte Rate-Begrenzung (Tokens werden mit dieser Rate aufgefüllt)
- `maxBurst`: Maximale Tokens, die Sie ansammeln können (Burst-Kapazität)
- `remaining`: Aktuell verfügbare Tokens (können bis zu `maxBurst` sein)
- `requestsPerMinute`: Dauerhaftes Rate-Limit (Tokens werden mit dieser Rate aufgefüllt)
- `maxBurst`: Maximale Tokens, die Sie akkumulieren können (Burst-Kapazität)
- `remaining`: Aktuell verfügbare Tokens (kann bis zu `maxBurst` betragen)
**Antwortfelder:**
- `currentPeriodCost` spiegelt die Nutzung in der aktuellen Abrechnungsperiode wider
- `limit` wird von individuellen Limits (Free/Pro) oder gepoolten Organisationslimits (Team/Enterprise) abgeleitet
- `plan` ist der aktive Plan mit der höchsten Priorität, der mit Ihrem Benutzer verknüpft ist
- `currentPeriodCost` spiegelt die Nutzung im aktuellen Abrechnungszeitraum wider
- `limit` wird aus individuellen Limits (Free/Pro) oder gepoolten Organisationslimits (Team/Enterprise) abgeleitet
- `plan` ist der Plan mit der höchsten Priorität, der Ihrem Benutzer zugeordnet ist
## Plan-Limits
Verschiedene Abonnementpläne haben unterschiedliche Nutzungslimits:
Verschiedene Abonnement-Pläne haben unterschiedliche Nutzungslimits:
| Plan | Monatliches Nutzungslimit | Rate-Limits (pro Minute) |
| Plan | Monatliches Nutzungslimit | Ratenlimits (pro Minute) |
|------|-------------------|-------------------------|
| **Free** | $10 | 5 sync, 10 async |
| **Pro** | $100 | 10 sync, 50 async |
| **Team** | $500 (gepoolt) | 50 sync, 100 async |
| **Free** | 20 $ | 5 sync, 10 async |
| **Pro** | 100 $ | 10 sync, 50 async |
| **Team** | 500 $ (gemeinsam) | 50 sync, 100 async |
| **Enterprise** | Individuell | Individuell |
## Abrechnungsmodell
Sim verwendet ein **Basisabonnement + Mehrverbrauch**-Abrechnungsmodell:
Sim verwendet ein **Basis-Abonnement + Mehrverbrauch**-Abrechnungsmodell:
### Wie es funktioniert
### So funktioniert es
**Pro-Plan ($20/Monat):**
- Monatliches Abonnement beinhaltet $20 Nutzung
- Nutzung unter $20 → Keine zusätzlichen Kosten
- Nutzung über $20 → Zahlen Sie den Mehrverbrauch am Monatsende
- Beispiel: $35 Nutzung = $20 (Abonnement) + $15 (Mehrverbrauch)
**Pro-Plan (20 $/Monat):**
- Monatsabonnement beinhaltet 20 $ Nutzung
- Nutzung unter 20 $ → Keine zusätzlichen Gebühren
- Nutzung über 20 $ → Mehrverbrauch am Monatsende zahlen
- Beispiel: 35 $ Nutzung = 20 $ (Abonnement) + 15 $ (Mehrverbrauch)
**Team-Plan ($40/Benutzer/Monat):**
- Gepoolte Nutzung für alle Teammitglieder
- Mehrverbrauch wird aus der Gesamtnutzung des Teams berechnet
**Team-Plan (40 $/Platz/Monat):**
- Gemeinsame Nutzung über alle Teammitglieder
- Mehrverbrauch wird aus der gesamten Team-Nutzung berechnet
- Organisationsinhaber erhält eine Rechnung
**Enterprise-Pläne:**
- Fester monatlicher Preis, kein Mehrverbrauch
- Fester Monatspreis, kein Mehrverbrauch
- Individuelle Nutzungslimits gemäß Vereinbarung
### Schwellenwert-Abrechnung
Wenn der nicht abgerechnete Mehrverbrauch $50 erreicht, berechnet Sim automatisch den gesamten nicht abgerechneten Betrag.
Wenn der nicht abgerechnete Mehrverbrauch 50 $ erreicht, rechnet Sim automatisch den gesamten nicht abgerechneten Betrag ab.
**Beispiel:**
- Tag 10: $70 Mehrverbrauch → Sofortige Abrechnung von $70
- Tag 15: Zusätzliche $35 Nutzung ($105 insgesamt) → Bereits abgerechnet, keine Aktion
- Tag 20: Weitere $50 Nutzung ($155 insgesamt, $85 nicht abgerechnet) → Sofortige Abrechnung von $85
- Tag 10: 70 $ Mehrverbrauch → 70 $ sofort abrechnen
- Tag 15: Zusätzliche 35 $ Nutzung (105 $ gesamt) → Bereits abgerechnet, keine Aktion
- Tag 20: Weitere 50 $ Nutzung (155 $ gesamt, 85 $ nicht abgerechnet) → 85 $ sofort abrechnen
Dies verteilt große Überziehungsgebühren über den Monat, anstatt eine große Rechnung am Ende des Abrechnungszeitraums zu erhalten.
Dies verteilt große Mehrverbrauchsgebühren über den Monat, anstatt einer großen Rechnung am Periodenende.
## Best Practices für Kostenmanagement
1. **Regelmäßig überwachen**: Überprüfen Sie Ihr Nutzungs-Dashboard häufig, um Überraschungen zu vermeiden
2. **Budgets festlegen**: Nutzen Sie Planlimits als Leitplanken für Ihre Ausgaben
2. **Budgets festlegen**: Nutzen Sie Plan-Limits als Leitplanken für Ihre Ausgaben
3. **Workflows optimieren**: Überprüfen Sie kostenintensive Ausführungen und optimieren Sie Prompts oder Modellauswahl
4. **Passende Modelle verwenden**: Passen Sie die Modellkomplexität an die Aufgabenanforderungen an
5. **Ähnliche Aufgaben bündeln**: Kombinieren Sie wenn möglich mehrere Anfragen, um den Overhead zu reduzieren
5. **Ähnliche Aufgaben bündeln**: Kombinieren Sie mehrere Anfragen, wenn möglich, um Overhead zu reduzieren
## Nächste Schritte
- Überprüfen Sie Ihre aktuelle Nutzung unter [Einstellungen → Abonnement](https://sim.ai/settings/subscription)
- Erfahren Sie mehr über [Protokollierung](/execution/logging), um Ausführungsdetails zu verfolgen
- Erkunden Sie die [Externe API](/execution/api) für programmatische Kostenüberwachung
- Sehen Sie sich [Workflow-Optimierungstechniken](/blocks) an, um Kosten zu reduzieren
- Entdecken Sie die [externe API](/execution/api) für programmatische Kostenüberwachung
- Sehen Sie sich [Workflow-Optimierungstechniken](/blocks) an, um Kosten zu reduzieren
**Pro-Tarif (20 $/Monat):**
- Monatliches Abonnement beinhaltet 20 $ Nutzung
- Nutzung unter 20 $ → Keine zusätzlichen Gebühren
- Nutzung über 20 $ → Mehrverbrauch wird am Monatsende abgerechnet
- Beispiel: 35 $ Nutzung = 20 $ (Abonnement) + 15 $ (Mehrverbrauch)
**Team-Tarif (40 $/Platz/Monat):**
- Gemeinsame Nutzung über alle Teammitglieder hinweg
- Mehrverbrauch wird aus der gesamten Teamnutzung berechnet
- Der Organisationsinhaber erhält eine Rechnung
**Enterprise-Tarife:**
- Fester Monatspreis, keine Mehrverbräuche
- Individuelle Nutzungslimits gemäß Vereinbarung
### Schwellenwertabrechnung
Wenn der nicht abgerechnete Mehrverbrauch 50 $ erreicht, rechnet Sim automatisch den gesamten nicht abgerechneten Betrag ab.
**Beispiel:**
- Tag 10: 70 $ Mehrverbrauch → 70 $ sofort abrechnen
- Tag 15: Weitere 35 $ Nutzung (105 $ gesamt) → Bereits abgerechnet, keine Aktion
- Tag 20: Weitere 50 $ Nutzung (155 $ gesamt, 85 $ nicht abgerechnet) → 85 $ sofort abrechnen
Dies verteilt hohe Mehrverbrauchsgebühren über den Monat hinweg, anstatt einer großen Rechnung am Periodenende.
## Best Practices für das Kostenmanagement
1. **Regelmäßig überwachen**: Überprüfen Sie Ihr Nutzungs-Dashboard häufig, um Überraschungen zu vermeiden
2. **Budgets festlegen**: Nutzen Sie Tariflimits als Leitplanken für Ihre Ausgaben
3. **Workflows optimieren**: Überprüfen Sie kostenintensive Ausführungen und optimieren Sie Prompts oder Modellauswahl
4. **Passende Modelle verwenden**: Stimmen Sie die Modellkomplexität auf die Aufgabenanforderungen ab
5. **Ähnliche Aufgaben bündeln**: Kombinieren Sie mehrere Anfragen, wenn möglich, um den Overhead zu reduzieren
## Nächste Schritte
- Überprüfen Sie Ihre aktuelle Nutzung unter [Einstellungen → Abonnement](https://sim.ai/settings/subscription)
- Erfahren Sie mehr über [Protokollierung](/execution/logging), um Ausführungsdetails zu verfolgen
- Erkunden Sie die [externe API](/execution/api) für programmatische Kostenüberwachung
- Informieren Sie sich über [Workflow-Optimierungstechniken](/blocks), um Kosten zu reduzieren

View File

@@ -0,0 +1,172 @@
---
title: Dateien übergeben
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
Sim macht es einfach, mit Dateien in Ihren Workflows zu arbeiten. Blöcke können Dateien empfangen, verarbeiten und nahtlos an andere Blöcke weitergeben.
## Dateiobjekte
Wenn Blöcke Dateien ausgeben (wie Gmail-Anhänge, generierte Bilder oder geparste Dokumente), geben sie ein standardisiertes Dateiobjekt zurück:
```json
{
"name": "report.pdf",
"url": "https://...",
"base64": "JVBERi0xLjQK...",
"type": "application/pdf",
"size": 245678
}
```
Sie können auf alle diese Eigenschaften zugreifen, wenn Sie auf Dateien aus vorherigen Blöcken verweisen.
## Der Datei-Block
Der **Datei-Block** ist der universelle Einstiegspunkt für Dateien in Ihren Workflows. Er akzeptiert Dateien aus jeder Quelle und gibt standardisierte Dateiobjekte aus, die mit allen Integrationen funktionieren.
**Eingaben:**
- **Hochgeladene Dateien** - Dateien direkt per Drag & Drop oder Auswahl hinzufügen
- **Externe URLs** - Jede öffentlich zugängliche Datei-URL
- **Dateien von anderen Blöcken** - Dateien von Gmail-Anhängen, Slack-Downloads usw. übergeben
**Ausgaben:**
- Eine Liste von `UserFile`-Objekten mit konsistenter Struktur (`name`, `url`, `base64`, `type`, `size`)
- `combinedContent` - Extrahierter Textinhalt aus allen Dateien (für Dokumente)
**Beispielverwendung:**
```
// Get all files from the File block
<file.files>
// Get the first file
<file.files[0]>
// Get combined text content from parsed documents
<file.combinedContent>
```
Der Datei-Block führt automatisch folgende Aktionen aus:
- Erkennt Dateitypen aus URLs und Erweiterungen
- Extrahiert Text aus PDFs, CSVs und Dokumenten
- Generiert Base64-Kodierung für Binärdateien
- Erstellt vorsignierte URLs für sicheren Zugriff
Verwenden Sie den Datei-Block, wenn Sie Dateien aus verschiedenen Quellen normalisieren müssen, bevor Sie sie an andere Blöcke wie Vision, STT oder E-Mail-Integrationen übergeben.
## Dateien zwischen Blöcken übergeben
Verweisen Sie auf Dateien aus vorherigen Blöcken über das Tag-Dropdown. Klicken Sie in ein beliebiges Dateieingabefeld und geben Sie `<` ein, um verfügbare Ausgaben anzuzeigen.
**Häufige Muster:**
```
// Single file from a block
<gmail.attachments[0]>
// Pass the whole file object
<file_parser.files[0]>
// Access specific properties
<gmail.attachments[0].name>
<gmail.attachments[0].base64>
```
Die meisten Blöcke akzeptieren das vollständige Dateiobjekt und extrahieren automatisch, was sie benötigen. Sie müssen `base64` oder `url` in den meisten Fällen nicht manuell extrahieren.
## Workflows mit Dateien auslösen
Wenn Sie einen Workflow über die API aufrufen, der Dateieingaben erwartet, fügen Sie Dateien in Ihre Anfrage ein:
<Tabs items={['Base64', 'URL']}>
<Tab value="Base64">
```bash
curl -X POST "https://sim.ai/api/workflows/YOUR_WORKFLOW_ID/execute" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"document": {
"name": "report.pdf",
"base64": "JVBERi0xLjQK...",
"type": "application/pdf"
}
}'
```
</Tab>
<Tab value="URL">
```bash
curl -X POST "https://sim.ai/api/workflows/YOUR_WORKFLOW_ID/execute" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"document": {
"name": "report.pdf",
"url": "https://example.com/report.pdf",
"type": "application/pdf"
}
}'
```
</Tab>
</Tabs>
Der Start-Block des Workflows sollte ein Eingabefeld haben, das für den Empfang des Dateiparameters konfiguriert ist.
## Dateien in API-Antworten empfangen
Wenn ein Workflow Dateien ausgibt, sind diese in der Antwort enthalten:
```json
{
"success": true,
"output": {
"generatedFile": {
"name": "output.png",
"url": "https://...",
"base64": "iVBORw0KGgo...",
"type": "image/png",
"size": 34567
}
}
}
```
Verwenden Sie `url` für direkte Downloads oder `base64` für Inline-Verarbeitung.
## Blöcke, die mit Dateien arbeiten
**Dateieingaben:**
- **File** - Dokumente, Bilder und Textdateien parsen
- **Vision** - Bilder mit KI-Modellen analysieren
- **Mistral Parser** - Text aus PDFs extrahieren
**Dateiausgaben:**
- **Gmail** - E-Mail-Anhänge
- **Slack** - Heruntergeladene Dateien
- **TTS** - Generierte Audiodateien
- **Video Generator** - Generierte Videos
- **Image Generator** - Generierte Bilder
**Dateispeicherung:**
- **Supabase** - Upload/Download aus dem Speicher
- **S3** - AWS S3-Operationen
- **Google Drive** - Drive-Dateioperationen
- **Dropbox** - Dropbox-Dateioperationen
<Callout type="info">
Dateien sind automatisch für nachgelagerte Blöcke verfügbar. Die Ausführungs-Engine übernimmt die gesamte Dateiübertragung und Formatkonvertierung.
</Callout>
## Best Practices
1. **Dateiobjekte direkt verwenden** - Übergeben Sie das vollständige Dateiobjekt, anstatt einzelne Eigenschaften zu extrahieren. Blöcke übernehmen die Konvertierung automatisch.
2. **Dateitypen prüfen** - Stellen Sie sicher, dass der Dateityp mit dem übereinstimmt, was der empfangende Block erwartet. Der Vision-Block benötigt Bilder, der File-Block verarbeitet Dokumente.
3. **Dateigröße beachten** Große Dateien erhöhen die Ausführungszeit. Bei sehr großen Dateien sollten Sie Storage-Blöcke (S3, Supabase) für die Zwischenspeicherung verwenden.

View File

@@ -0,0 +1,142 @@
---
title: Formular-Bereitstellung
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
Stellen Sie Ihren Workflow als einbettbares Formular bereit, das Benutzer auf Ihrer Website ausfüllen oder per Link teilen können. Formularübermittlungen lösen Ihren Workflow mit dem `form` Trigger-Typ aus.
## Übersicht
Die Formular-Bereitstellung verwandelt das Eingabeformat Ihres Workflows in ein responsives Formular, das:
- Per Direktlink geteilt werden kann (z. B. `https://sim.ai/form/my-survey`)
- Mit einem iframe in jede Website eingebettet werden kann
Wenn ein Benutzer das Formular absendet, wird Ihr Workflow mit den Formulardaten ausgelöst.
<Callout type="info">
Formulare leiten ihre Felder vom Eingabeformat des Start-Blocks Ihres Workflows ab. Jedes Feld wird zu einer Formulareingabe mit dem entsprechenden Typ.
</Callout>
## Erstellen eines Formulars
1. Öffnen Sie Ihren Workflow und klicken Sie auf **Bereitstellen**
2. Wählen Sie den Tab **Formular**
3. Konfigurieren Sie:
- **URL**: Eindeutige Kennung (z. B. `contact-form` → `sim.ai/form/contact-form`)
- **Titel**: Formularüberschrift
- **Beschreibung**: Optionaler Untertitel
- **Formularfelder**: Passen Sie Beschriftungen und Beschreibungen für jedes Feld an
- **Authentifizierung**: Öffentlich, passwortgeschützt oder E-Mail-Whitelist
- **Dankesnachricht**: Wird nach der Übermittlung angezeigt
4. Klicken Sie auf **Starten**
## Feldzuordnung
| Eingabeformat-Typ | Formularfeld |
|------------------|------------|
| `string` | Texteingabe |
| `number` | Zahleneingabe |
| `boolean` | Umschalter |
| `object` | JSON-Editor |
| `array` | JSON-Array-Editor |
| `files` | Datei-Upload |
## Zugriffskontrolle
| Modus | Beschreibung |
|------|-------------|
| **Öffentlich** | Jeder mit dem Link kann absenden |
| **Passwort** | Benutzer müssen ein Passwort eingeben |
| **E-Mail-Whitelist** | Nur angegebene E-Mails/Domains können absenden |
Für E-Mail-Whitelist:
- Exakt: `user@example.com`
- Domain: `@example.com` (alle E-Mails von der Domain)
## Einbettung
### Direkter Link
```
https://sim.ai/form/your-identifier
```
### Iframe
```html
<iframe
src="https://sim.ai/form/your-identifier"
width="100%"
height="600"
frameborder="0"
title="Form"
></iframe>
```
## API-Übermittlung
Formulare programmatisch übermitteln:
<Tabs items={['cURL', 'TypeScript']}>
<Tab value="cURL">
```bash
curl -X POST https://sim.ai/api/form/your-identifier \
-H "Content-Type: application/json" \
-d '{
"formData": {
"name": "John Doe",
"email": "john@example.com"
}
}'
```
</Tab>
<Tab value="TypeScript">
```typescript
const response = await fetch('https://sim.ai/api/form/your-identifier', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
formData: {
name: 'John Doe',
email: 'john@example.com'
}
})
});
const result = await response.json();
// { success: true, data: { executionId: '...' } }
```
</Tab>
</Tabs>
### Geschützte Formulare
Für passwortgeschützte Formulare:
```bash
curl -X POST https://sim.ai/api/form/your-identifier \
-H "Content-Type: application/json" \
-d '{ "password": "secret", "formData": { "name": "John" } }'
```
Für E-Mail-geschützte Formulare:
```bash
curl -X POST https://sim.ai/api/form/your-identifier \
-H "Content-Type: application/json" \
-d '{ "email": "allowed@example.com", "formData": { "name": "John" } }'
```
## Fehlerbehebung
**"Keine Eingabefelder konfiguriert"** - Fügen Sie Eingabeformat-Felder zu Ihrem Start-Block hinzu.
**Formular lädt nicht im Iframe** - Überprüfen Sie, ob die CSP Ihrer Website Iframes von `sim.ai` erlaubt.
**Übermittlungen schlagen fehl** - Überprüfen Sie, ob die Kennung korrekt ist und erforderliche Felder ausgefüllt sind.

View File

@@ -0,0 +1,60 @@
---
title: Tastaturkürzel
description: Meistern Sie die Workflow-Arbeitsfläche mit Tastaturkürzeln und Maussteuerung
---
import { Callout } from 'fumadocs-ui/components/callout'
Beschleunigen Sie die Erstellung Ihrer Workflows mit diesen Tastaturkürzeln und Maussteuerungen. Alle Tastenkombinationen funktionieren, wenn die Arbeitsfläche fokussiert ist (nicht beim Tippen in einem Eingabefeld).
<Callout type="info">
**Mod** bezieht sich auf `Cmd` unter macOS und `Ctrl` unter Windows/Linux.
</Callout>
## Arbeitsflächen-Steuerung
### Maussteuerung
| Aktion | Steuerung |
|--------|---------|
| Arbeitsfläche verschieben | Linksziehen auf leerer Fläche |
| Arbeitsfläche verschieben | Scrollen oder Trackpad |
| Mehrere Blöcke auswählen | Rechtsziehen zum Aufziehen eines Auswahlrahmens |
| Block ziehen | Linksziehen auf Block-Kopfzeile |
| Zur Auswahl hinzufügen | `Mod` + Klick auf Blöcke |
### Workflow-Aktionen
| Tastenkombination | Aktion |
|----------|--------|
| `Mod` + `Enter` | Workflow ausführen (oder abbrechen, falls aktiv) |
| `Mod` + `Z` | Rückgängig |
| `Mod` + `Shift` + `Z` | Wiederholen |
| `Mod` + `C` | Ausgewählte Blöcke kopieren |
| `Mod` + `V` | Blöcke einfügen |
| `Delete` oder `Backspace` | Ausgewählte Blöcke oder Verbindungen löschen |
| `Shift` + `L` | Arbeitsfläche automatisch anordnen |
## Panel-Navigation
Diese Tastenkombinationen wechseln zwischen den Panel-Tabs auf der rechten Seite der Arbeitsfläche.
| Tastenkombination | Aktion |
|----------|--------|
| `Mod` + `F` | Toolbar-Suche fokussieren |
## Globale Navigation
| Tastenkombination | Aktion |
|----------|--------|
| `Mod` + `K` | Suche öffnen |
| `Mod` + `Shift` + `A` | Neuen Agenten-Workflow hinzufügen |
| `Mod` + `Y` | Zu Vorlagen gehen |
| `Mod` + `L` | Zu Logs gehen |
## Dienstprogramm
| Tastenkombination | Aktion |
|----------|--------|
| `Mod` + `D` | Terminal-Konsole leeren |
| `Mod` + `E` | Benachrichtigungen löschen |

View File

@@ -35,81 +35,87 @@ Sobald Ihre Dokumente verarbeitet sind, können Sie die einzelnen Chunks anzeige
<Image src="/static/knowledgebase/knowledgebase.png" alt="Dokumentchunk-Ansicht mit verarbeiteten Inhalten" width={800} height={500} />
### Chunk-Konfiguration
- **Standardgröße der Chunks**: 1.024 Zeichen
- **Konfigurierbarer Bereich**: 100-4.000 Zeichen pro Chunk
- **Intelligente Überlappung**: Standardmäßig 200 Zeichen zur Kontexterhaltung
- **Hierarchische Aufteilung**: Respektiert Dokumentstruktur (Abschnitte, Absätze, Sätze)
### Bearbeitungsfunktionen
Beim Erstellen einer Wissensdatenbank können Sie konfigurieren, wie Dokumente in Chunks aufgeteilt werden:
| Einstellung | Einheit | Standard | Bereich | Beschreibung |
|---------|------|---------|-------|-------------|
| **Maximale Chunk-Größe** | Tokens | 1.024 | 100-4.000 | Maximale Größe jedes Chunks (1 Token ≈ 4 Zeichen) |
| **Minimale Chunk-Größe** | Zeichen | 1 | 1-2.000 | Minimale Chunk-Größe, um winzige Fragmente zu vermeiden |
| **Überlappung** | Zeichen | 200 | 0-500 | Kontextüberlappung zwischen aufeinanderfolgenden Chunks |
- **Hierarchische Aufteilung**: Berücksichtigt die Dokumentstruktur (Abschnitte, Absätze, Sätze)
### Bearbeitungsmöglichkeiten
- **Chunk-Inhalt bearbeiten**: Textinhalt einzelner Chunks ändern
- **Chunk-Grenzen anpassen**: Chunks bei Bedarf zusammenführen oder teilen
- **Chunk-Grenzen anpassen**: Chunks nach Bedarf zusammenführen oder aufteilen
- **Metadaten hinzufügen**: Chunks mit zusätzlichem Kontext anreichern
- **Massenoperationen**: Effiziente Verwaltung mehrerer Chunks
- **Massenoperationen**: Mehrere Chunks effizient verwalten
## Erweiterte PDF-Verarbeitung
Für PDF-Dokumente bietet Sim erweiterte Verarbeitungsfunktionen:
### OCR-Unterstützung
Bei Konfiguration mit Azure oder [Mistral OCR](https://docs.mistral.ai/ocr/):
Wenn mit Azure oder [Mistral OCR](https://docs.mistral.ai/ocr/) konfiguriert:
- **Verarbeitung gescannter Dokumente**: Text aus bildbasierten PDFs extrahieren
- **Umgang mit gemischten Inhalten**: Verarbeitung von PDFs mit Text und Bildern
- **Verarbeitung gemischter Inhalte**: PDFs mit Text und Bildern verarbeiten
- **Hohe Genauigkeit**: Fortschrittliche KI-Modelle gewährleisten präzise Textextraktion
## Verwendung des Wissensblocks in Workflows
## Verwendung des Knowledge-Blocks in Workflows
Sobald Ihre Dokumente verarbeitet sind, können Sie sie in Ihren KI-Workflows über den Wissensblock nutzen. Dies ermöglicht Retrieval-Augmented Generation (RAG), wodurch Ihre KI-Agenten auf Ihre Dokumentinhalte zugreifen und darüber nachdenken können, um genauere, kontextbezogene Antworten zu liefern.
Sobald Ihre Dokumente verarbeitet sind, können Sie sie in Ihren KI-Workflows über den Knowledge-Block verwenden. Dies ermöglicht Retrieval-Augmented Generation (RAG), wodurch Ihre KI-Agenten auf Ihre Dokumentinhalte zugreifen und darüber nachdenken können, um genauere, kontextbezogene Antworten zu liefern.
<Image src="/static/knowledgebase/knowledgebase-2.png" alt="Verwendung des Wissensblocks in Workflows" width={800} height={500} />
<Image src="/static/knowledgebase/knowledgebase-2.png" alt="Verwendung des Knowledge-Blocks in Workflows" width={800} height={500} />
### Funktionen des Wissensblocks
- **Semantische Suche**: Relevante Inhalte mit natürlichsprachlichen Abfragen finden
- **Kontextintegration**: Automatisches Einbinden relevanter Chunks in Agenten-Prompts
- **Dynamischer Abruf**: Suche erfolgt in Echtzeit während der Workflow-Ausführung
- **Relevanzbewertung**: Ergebnisse nach semantischer Ähnlichkeit geordnet
### Knowledge-Block-Funktionen
- **Semantische Suche**: Relevante Inhalte mithilfe natürlichsprachlicher Abfragen finden
- **Kontextintegration**: Relevante Chunks automatisch in Agenten-Prompts einbinden
- **Dynamisches Abrufen**: Suche erfolgt in Echtzeit während der Workflow-Ausführung
- **Relevanz-Bewertung**: Ergebnisse nach semantischer Ähnlichkeit sortiert
### Integrationsoptionen
- **System-Prompts**: Kontext für Ihre KI-Agenten bereitstellen
- **Dynamischer Kontext**: Suche und Einbindung relevanter Informationen während Gesprächen
- **Dokumentübergreifende Suche**: Abfrage über Ihre gesamte Wissensdatenbank
- **Gefilterte Suche**: Kombination mit Tags für präzisen Inhaltsabruf
- **System-Prompts**: Stellen Sie Ihren KI-Agenten Kontext bereit
- **Dynamischer Kontext**: Suchen und fügen Sie relevante Informationen während Konversationen hinzu
- **Multi-Dokument-Suche**: Durchsuchen Sie Ihre gesamte Wissensdatenbank
- **Gefilterte Suche**: Kombinieren Sie mit Tags für präzises Abrufen von Inhalten
## Vektorsuchtechnologie
## Vektor-Suchtechnologie
Sim verwendet Vektorsuche, die von [pgvector](https://github.com/pgvector/pgvector) unterstützt wird, um die Bedeutung und den Kontext Ihrer Inhalte zu verstehen:
### Semantisches Verständnis
- **Kontextuelle Suche**: Findet relevante Inhalte, auch wenn exakte Schlüsselwörter nicht übereinstimmen
- **Konzeptbasierte Abfrage**: Versteht Beziehungen zwischen Ideen
- **Konzeptbasiertes Abrufen**: Versteht Beziehungen zwischen Ideen
- **Mehrsprachige Unterstützung**: Funktioniert über verschiedene Sprachen hinweg
- **Synonymerkennung**: Findet verwandte Begriffe und Konzepte
### Suchfunktionen
- **Natürlichsprachige Abfragen**: Stellen Sie Fragen in natürlicher Sprache
- **Natürlichsprachige Abfragen**: Stellen Sie Fragen in einfachem Deutsch
- **Ähnlichkeitssuche**: Finden Sie konzeptionell ähnliche Inhalte
- **Hybridsuche**: Kombiniert Vektor- und traditionelle Schlüsselwortsuche
- **Konfigurierbare Ergebnisse**: Steuern Sie die Anzahl und den Relevanz-Schwellenwert der Ergebnisse
- **Hybride Suche**: Kombiniert Vektor- und traditionelle Schlüsselwortsuche
- **Konfigurierbare Ergebnisse**: Steuern Sie die Anzahl und Relevanzschwelle der Ergebnisse
## Dokumentenverwaltung
### Organisationsfunktionen
- **Massenupload**: Laden Sie mehrere Dateien gleichzeitig über die asynchrone API hoch
- **Verarbeitungsstatus**: Echtzeit-Updates zum Dokumentenverarbeitungsprozess
- **Suchen und Filtern**: Finden Sie Dokumente schnell in großen Sammlungen
- **Massen-Upload**: Laden Sie mehrere Dateien gleichzeitig über die asynchrone API hoch
- **Verarbeitungsstatus**: Echtzeit-Updates zur Dokumentenverarbeitung
- **Suchen und filtern**: Finden Sie Dokumente schnell in großen Sammlungen
- **Metadaten-Tracking**: Automatische Erfassung von Dateiinformationen und Verarbeitungsdetails
### Sicherheit und Datenschutz
- **Sichere Speicherung**: Dokumente werden mit Sicherheit auf Unternehmensniveau gespeichert
- **Zugriffskontrolle**: Workspace-basierte Berechtigungen
- **Verarbeitungsisolierung**: Jeder Workspace hat eine isolierte Dokumentenverarbeitung
- **Verarbeitungsisolierung**: Jeder Workspace hat isolierte Dokumentenverarbeitung
- **Datenaufbewahrung**: Konfigurieren Sie Richtlinien zur Dokumentenaufbewahrung
## Erste Schritte
1. **Navigieren Sie zu Ihrer Wissensdatenbank**: Zugriff über Ihre Workspace-Seitenleiste
2. **Dokumente hochladen**: Drag & Drop oder wählen Sie Dateien zum Hochladen aus
3. **Verarbeitung überwachen**: Beobachten Sie, wie Dokumente verarbeitet und in Chunks aufgeteilt werden
4. **Chunks erkunden**: Sehen und bearbeiten Sie die verarbeiteten Inhalte
5. **Zu Workflows hinzufügen**: Verwenden Sie den Wissensblock, um ihn in Ihre KI-Agenten zu integrieren
2. **Dokumente hochladen**: Ziehen und ablegen oder Dateien zum Hochladen auswählen
3. **Verarbeitung überwachen**: Beobachten Sie, wie Dokumente verarbeitet und in Abschnitte unterteilt werden
4. **Abschnitte erkunden**: Zeigen Sie die verarbeiteten Inhalte an und bearbeiten Sie sie
5. **Zu Workflows hinzufügen**: Verwenden Sie den Knowledge-Block, um mit Ihren KI-Agenten zu integrieren
Die Wissensdatenbank verwandelt Ihre statischen Dokumente in eine intelligente, durchsuchbare Ressource, die Ihre KI-Workflows für fundiertere und kontextbezogenere Antworten nutzen können.
Die Wissensdatenbank verwandelt Ihre statischen Dokumente in eine intelligente, durchsuchbare Ressource, die Ihre KI-Workflows für fundiertere und kontextbezogene Antworten nutzen können.

View File

@@ -0,0 +1,108 @@
---
title: Workflows als MCP bereitstellen
description: Stellen Sie Ihre Workflows als MCP-Tools für externe KI-Assistenten
und Anwendungen bereit
---
import { Video } from '@/components/ui/video'
import { Callout } from 'fumadocs-ui/components/callout'
Stellen Sie Ihre Workflows als MCP-Tools bereit, um sie für externe KI-Assistenten wie Claude Desktop, Cursor und andere MCP-kompatible Clients zugänglich zu machen. Dies verwandelt Ihre Workflows in aufrufbare Tools, die von überall aus aufgerufen werden können.
## MCP-Server erstellen und verwalten
MCP-Server gruppieren Ihre Workflow-Tools zusammen. Erstellen und verwalten Sie sie in den Workspace-Einstellungen:
<div className="mx-auto w-full overflow-hidden rounded-lg">
<Video src="mcp/mcp-server.mp4" width={700} height={450} />
</div>
1. Navigieren Sie zu **Einstellungen → MCP-Server**
2. Klicken Sie auf **Server erstellen**
3. Geben Sie einen Namen und eine optionale Beschreibung ein
4. Kopieren Sie die Server-URL zur Verwendung in Ihren MCP-Clients
5. Zeigen Sie alle zum Server hinzugefügten Tools an und verwalten Sie diese
## Einen Workflow als Tool hinzufügen
Sobald Ihr Workflow bereitgestellt ist, können Sie ihn als MCP-Tool verfügbar machen:
<div className="mx-auto w-full overflow-hidden rounded-lg">
<Video src="mcp/mcp-deploy-tool.mp4" width={700} height={450} />
</div>
1. Öffnen Sie Ihren bereitgestellten Workflow
2. Klicken Sie auf **Bereitstellen** und wechseln Sie zum Tab **MCP**
3. Konfigurieren Sie den Tool-Namen und die Beschreibung
4. Fügen Sie Beschreibungen für jeden Parameter hinzu (hilft der KI, Eingaben zu verstehen)
5. Wählen Sie aus, zu welchen MCP-Servern es hinzugefügt werden soll
<Callout type="info">
Der Workflow muss bereitgestellt sein, bevor er als MCP-Tool hinzugefügt werden kann.
</Callout>
## Tool-Konfiguration
### Tool-Name
Verwenden Sie Kleinbuchstaben, Zahlen und Unterstriche. Der Name sollte beschreibend sein und den MCP-Namenskonventionen folgen (z. B. `search_documents`, `send_email`).
### Beschreibung
Schreiben Sie eine klare Beschreibung dessen, was das Tool tut. Dies hilft KI-Assistenten zu verstehen, wann das Tool verwendet werden soll.
### Parameter
Die Eingabeformatfelder deines Workflows werden zu Tool-Parametern. Füge jedem Parameter Beschreibungen hinzu, um KI-Assistenten zu helfen, korrekte Werte bereitzustellen.
## MCP-Clients verbinden
Verwende die Server-URL aus den Einstellungen, um externe Anwendungen zu verbinden:
### Claude Desktop
Füge dies zu deiner Claude Desktop-Konfiguration hinzu (`~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"my-sim-workflows": {
"command": "npx",
"args": ["-y", "mcp-remote", "YOUR_SERVER_URL"]
}
}
}
```
### Cursor
Füge die Server-URL in den MCP-Einstellungen von Cursor mit demselben mcp-remote-Muster hinzu.
<Callout type="warn">
Füge deinen API-Key-Header (`X-API-Key`) für authentifizierten Zugriff hinzu, wenn du mcp-remote oder andere HTTP-basierte MCP-Transporte verwendest.
</Callout>
## Server-Verwaltung
In der Server-Detailansicht unter **Einstellungen → MCP-Server** können Sie:
- **Tools anzeigen**: Alle Workflows sehen, die einem Server hinzugefügt wurden
- **URL kopieren**: Die Server-URL für MCP-Clients abrufen
- **Workflows hinzufügen**: Weitere bereitgestellte Workflows als Tools hinzufügen
- **Tools entfernen**: Workflows vom Server entfernen
- **Server löschen**: Den gesamten Server und alle seine Tools entfernen
## So funktioniert es
Wenn ein MCP-Client dein Tool aufruft:
1. Die Anfrage wird an deiner MCP-Server-URL empfangen
2. Sim validiert die Anfrage und ordnet Parameter den Workflow-Eingaben zu
3. Der bereitgestellte Workflow wird mit den angegebenen Eingaben ausgeführt
4. Die Ergebnisse werden an den MCP-Client zurückgegeben
Workflows werden mit derselben Bereitstellungsversion wie API-Aufrufe ausgeführt, was konsistentes Verhalten gewährleistet.
## Berechtigungsanforderungen
| Aktion | Erforderliche Berechtigung |
|--------|-------------------|
| MCP-Server erstellen | **Admin** |
| Workflows zu Servern hinzufügen | **Write** oder **Admin** |
| MCP-Server anzeigen | **Read**, **Write** oder **Admin** |
| MCP-Server löschen | **Admin** |

View File

@@ -1,8 +1,10 @@
---
title: MCP (Model Context Protocol)
title: MCP-Tools verwenden
description: Externe Tools und Dienste über das Model Context Protocol verbinden
---
import { Image } from '@/components/ui/image'
import { Video } from '@/components/ui/video'
import { Callout } from 'fumadocs-ui/components/callout'
Das Model Context Protocol ([MCP](https://modelcontextprotocol.com/)) ermöglicht es Ihnen, externe Tools und Dienste über ein standardisiertes Protokoll zu verbinden, wodurch Sie APIs und Dienste direkt in Ihre Workflows integrieren können. Mit MCP können Sie die Fähigkeiten von Sim erweitern, indem Sie benutzerdefinierte Integrationen hinzufügen, die nahtlos mit Ihren Agenten und Workflows zusammenarbeiten.
@@ -20,14 +22,8 @@ MCP ist ein offener Standard, der es KI-Assistenten ermöglicht, sich sicher mit
MCP-Server stellen Sammlungen von Tools bereit, die Ihre Agenten nutzen können. Konfigurieren Sie diese in den Workspace-Einstellungen:
<div className="flex justify-center">
<Image
src="/static/blocks/mcp-1.png"
alt="Konfiguration eines MCP-Servers in den Einstellungen"
width={700}
height={450}
className="my-6"
/>
<div className="mx-auto w-full overflow-hidden rounded-lg">
<Video src="mcp/settings-mcp-tools.mp4" width={700} height={450} />
</div>
1. Navigieren Sie zu Ihren Workspace-Einstellungen
@@ -40,14 +36,18 @@ MCP-Server stellen Sammlungen von Tools bereit, die Ihre Agenten nutzen können.
Sie können MCP-Server auch direkt über die Symbolleiste in einem Agent-Block für eine schnelle Einrichtung konfigurieren.
</Callout>
## Verwendung von MCP-Tools in Agenten
### Tools aktualisieren
Sobald MCP-Server konfiguriert sind, werden ihre Tools innerhalb Ihrer Agent-Blöcke verfügbar:
Klicken Sie bei einem Server auf **Aktualisieren**, um die neuesten Tool-Schemas abzurufen und alle Agent-Blöcke, die diese Tools verwenden, automatisch mit den neuen Parameterdefinitionen zu aktualisieren.
## MCP-Tools in Agents verwenden
Sobald MCP-Server konfiguriert sind, werden ihre Tools in Ihren Agent-Blöcken verfügbar:
<div className="flex justify-center">
<Image
src="/static/blocks/mcp-2.png"
alt="Verwendung eines MCP-Tools im Agent-Block"
alt="Using MCP Tool in Agent Block"
width={700}
height={450}
className="my-6"
@@ -55,25 +55,25 @@ Sobald MCP-Server konfiguriert sind, werden ihre Tools innerhalb Ihrer Agent-Bl
</div>
1. Öffnen Sie einen **Agent**-Block
2. Im Abschnitt **Tools** sehen Sie die verfügbaren MCP-Tools
2. Im Bereich **Tools** sehen Sie die verfügbaren MCP-Tools
3. Wählen Sie die Tools aus, die der Agent verwenden soll
4. Der Agent kann nun während der Ausführung auf diese Tools zugreifen
## Eigenständiger MCP-Tool-Block
Für eine genauere Kontrolle können Sie den dedizierten MCP-Tool-Block verwenden, um bestimmte MCP-Tools auszuführen:
Für eine präzisere Steuerung können Sie den dedizierten MCP-Tool-Block verwenden, um bestimmte MCP-Tools auszuführen:
<div className="flex justify-center">
<Image
src="/static/blocks/mcp-3.png"
alt="Eigenständiger MCP-Tool-Block"
alt="Standalone MCP Tool Block"
width={700}
height={450}
className="my-6"
/>
</div>
Der MCP-Tool-Block ermöglicht es Ihnen:
Der MCP-Tool-Block ermöglicht Ihnen:
- Jedes konfigurierte MCP-Tool direkt auszuführen
- Spezifische Parameter an das Tool zu übergeben
- Die Ausgabe des Tools in nachfolgenden Workflow-Schritten zu verwenden
@@ -81,9 +81,9 @@ Der MCP-Tool-Block ermöglicht es Ihnen:
### Wann MCP-Tool vs. Agent verwenden
**Verwenden Sie einen Agenten mit MCP-Tools, wenn:**
- Sie möchten, dass die KI entscheidet, welche Tools zu verwenden sind
- Sie komplexe Überlegungen benötigen, wann und wie Tools eingesetzt werden sollen
**Verwenden Sie Agent mit MCP-Tools, wenn:**
- Sie möchten, dass die KI entscheidet, welche Tools verwendet werden
- Sie komplexes Reasoning darüber benötigen, wann und wie Tools verwendet werden
- Sie eine natürlichsprachliche Interaktion mit den Tools wünschen
**Verwenden Sie den MCP-Tool-Block, wenn:**
@@ -93,7 +93,7 @@ Der MCP-Tool-Block ermöglicht es Ihnen:
## Berechtigungsanforderungen
MCP-Funktionalität erfordert spezifische Workspace-Berechtigungen:
Die MCP-Funktionalität erfordert spezifische Workspace-Berechtigungen:
| Aktion | Erforderliche Berechtigung |
|--------|-------------------|
@@ -105,7 +105,7 @@ MCP-Funktionalität erfordert spezifische Workspace-Berechtigungen:
## Häufige Anwendungsfälle
### Datenbankintegration
Verbinden Sie sich mit Datenbanken, um Daten innerhalb Ihrer Workflows abzufragen, einzufügen oder zu aktualisieren.
Verbinden Sie sich mit Datenbanken, um Daten in Ihren Workflows abzufragen, einzufügen oder zu aktualisieren.
### API-Integrationen
Greifen Sie auf externe APIs und Webdienste zu, die keine integrierten Sim-Integrationen haben.
@@ -113,8 +113,8 @@ Greifen Sie auf externe APIs und Webdienste zu, die keine integrierten Sim-Integ
### Dateisystemzugriff
Lesen, schreiben und bearbeiten Sie Dateien auf lokalen oder entfernten Dateisystemen.
### Benutzerdefinierte Geschäftslogik
Führen Sie benutzerdefinierte Skripte oder Tools aus, die auf die Bedürfnisse Ihrer Organisation zugeschnitten sind.
### Individuelle Geschäftslogik
Führen Sie benutzerdefinierte Skripte oder Tools aus, die spezifisch für die Anforderungen Ihrer Organisation sind.
### Echtzeit-Datenzugriff
Rufen Sie Live-Daten von externen Systemen während der Workflow-Ausführung ab.
@@ -128,12 +128,12 @@ Rufen Sie Live-Daten von externen Systemen während der Workflow-Ausführung ab.
## Fehlerbehebung
### MCP-Server erscheint nicht
### MCP-Server wird nicht angezeigt
- Überprüfen Sie, ob die Serverkonfiguration korrekt ist
- Prüfen Sie, ob Sie die erforderlichen Berechtigungen haben
- Stellen Sie sicher, dass der MCP-Server läuft und zugänglich ist
- Prüfen Sie, ob Sie über die erforderlichen Berechtigungen verfügen
- Stellen Sie sicher, dass der MCP-Server läuft und erreichbar ist
### Fehler bei der Tool-Ausführung
### Tool-Ausführungsfehler
- Überprüfen Sie, ob die Tool-Parameter korrekt formatiert sind
- Prüfen Sie die MCP-Server-Logs auf Fehlermeldungen
- Stellen Sie sicher, dass die erforderliche Authentifizierung konfiguriert ist
@@ -141,4 +141,4 @@ Rufen Sie Live-Daten von externen Systemen während der Workflow-Ausführung ab.
### Berechtigungsfehler
- Bestätigen Sie Ihre Workspace-Berechtigungsstufe
- Prüfen Sie, ob der MCP-Server zusätzliche Authentifizierung erfordert
- Stellen Sie sicher, dass der Server für Ihren Workspace richtig konfiguriert ist
- Überprüfen Sie, ob der Server ordnungsgemäß für Ihren Workspace konfiguriert ist

View File

@@ -0,0 +1,24 @@
{
"title": "Sim Documentation",
"pages": [
"./introduction/index",
"./getting-started/index",
"./quick-reference/index",
"triggers",
"blocks",
"tools",
"connections",
"mcp",
"copilot",
"skills",
"knowledgebase",
"variables",
"credentials",
"execution",
"permissions",
"self-hosting",
"./enterprise/index",
"./keyboard-shortcuts/index"
],
"defaultOpen": false
}

View File

@@ -0,0 +1,394 @@
---
title: Kurzreferenz
description: Wesentliche Aktionen zum Navigieren und Verwenden des Sim-Workflow-Editors
---
import { Callout } from 'fumadocs-ui/components/callout'
import { ActionImage, ActionVideo } from '@/components/ui/action-media'
Eine schnelle Übersicht für alltägliche Aktionen im Sim-Workflow-Editor. Für Tastaturkürzel siehe [Tastaturkürzel](/keyboard-shortcuts).
<Callout type="info">
**Mod** bezieht sich auf `Cmd` unter macOS und `Ctrl` unter Windows/Linux.
</Callout>
## Arbeitsbereiche
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Arbeitsbereich erstellen</td>
<td>Arbeitsbereich-Dropdown anklicken → **Neuer Arbeitsbereich**</td>
<td><ActionVideo src="quick-reference/create-workspace.mp4" alt="Arbeitsbereich erstellen" /></td>
</tr>
<tr>
<td>Arbeitsbereiche wechseln</td>
<td>Arbeitsbereich-Dropdown anklicken → Arbeitsbereich auswählen</td>
<td><ActionVideo src="quick-reference/switch-workspace.mp4" alt="Arbeitsbereiche wechseln" /></td>
</tr>
<tr>
<td>Teammitglieder einladen</td>
<td>Seitenleiste → **Einladen**</td>
<td><ActionVideo src="quick-reference/invite.mp4" alt="Teammitglieder einladen" /></td>
</tr>
<tr>
<td>Arbeitsbereich umbenennen</td>
<td>Rechtsklick auf Arbeitsbereich → **Umbenennen**</td>
<td rowSpan={4}><ActionImage src="/static/quick-reference/workspace-context-menu.png" alt="Arbeitsbereich-Kontextmenü" /></td>
</tr>
<tr>
<td>Arbeitsbereich duplizieren</td>
<td>Rechtsklick auf Arbeitsbereich → **Duplizieren**</td>
</tr>
<tr>
<td>Arbeitsbereich exportieren</td>
<td>Rechtsklick auf Arbeitsbereich → **Exportieren**</td>
</tr>
<tr>
<td>Arbeitsbereich löschen</td>
<td>Rechtsklick auf Arbeitsbereich → **Löschen**</td>
</tr>
</tbody>
</table>
## Workflows
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Workflow erstellen</td>
<td>**+**-Schaltfläche in der Seitenleiste anklicken</td>
<td><ActionImage src="/static/quick-reference/create-workflow.png" alt="Workflow erstellen" /></td>
</tr>
<tr>
<td>Workflows neu anordnen / verschieben</td>
<td>Workflow nach oben/unten oder auf einen Ordner ziehen</td>
<td><ActionVideo src="quick-reference/reordering.mp4" alt="Workflows neu anordnen" /></td>
</tr>
<tr>
<td>Workflow importieren</td>
<td>Import-Schaltfläche in der Seitenleiste anklicken → Datei auswählen</td>
<td><ActionImage src="/static/quick-reference/import-workflow.png" alt="Workflow importieren" /></td>
</tr>
<tr>
<td>Mehrere Workflows auswählen</td>
<td>`Mod+Click` oder `Shift+Click` Workflows in der Seitenleiste</td>
<td><ActionVideo src="quick-reference/multiselect.mp4" alt="Mehrere Workflows auswählen" /></td>
</tr>
<tr>
<td>In neuem Tab öffnen</td>
<td>Rechtsklick auf Workflow → **In neuem Tab öffnen**</td>
<td rowSpan={6}><ActionImage src="/static/quick-reference/workflow-context-menu.png" alt="Workflow-Kontextmenü" /></td>
</tr>
<tr>
<td>Workflow umbenennen</td>
<td>Rechtsklick auf Workflow → **Umbenennen**</td>
</tr>
<tr>
<td>Workflow-Farbe zuweisen</td>
<td>Rechtsklick auf Workflow → **Farbe ändern**</td>
</tr>
<tr>
<td>Workflow duplizieren</td>
<td>Rechtsklick auf Workflow → **Duplizieren**</td>
</tr>
<tr>
<td>Workflow exportieren</td>
<td>Rechtsklick auf Workflow → **Exportieren**</td>
</tr>
<tr>
<td>Workflow löschen</td>
<td>Rechtsklick auf Workflow → **Löschen**</td>
</tr>
<tr>
<td>Ordner umbenennen</td>
<td>Rechtsklick auf Ordner → **Umbenennen**</td>
<td rowSpan={6}><ActionImage src="/static/quick-reference/folder-context-menu.png" alt="Ordner-Kontextmenü" /></td>
</tr>
<tr>
<td>Workflow in Ordner erstellen</td>
<td>Rechtsklick auf Ordner → **Workflow erstellen**</td>
</tr>
<tr>
<td>Ordner in Ordner erstellen</td>
<td>Rechtsklick auf Ordner → **Ordner erstellen**</td>
</tr>
<tr>
<td>Ordner duplizieren</td>
<td>Rechtsklick auf Ordner → **Duplizieren**</td>
</tr>
<tr>
<td>Ordner exportieren</td>
<td>Rechtsklick auf Ordner → **Exportieren**</td>
</tr>
<tr>
<td>Ordner löschen</td>
<td>Rechtsklick auf Ordner → **Löschen**</td>
</tr>
</tbody>
</table>
## Blöcke
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Block hinzufügen</td>
<td>Aus Toolbar-Panel ziehen oder Rechtsklick auf Canvas → **Block hinzufügen**</td>
<td><ActionVideo src="quick-reference/add-block.mp4" alt="Block hinzufügen" /></td>
</tr>
<tr>
<td>Mehrere Blöcke auswählen</td>
<td>`Mod+Click` zusätzliche Blöcke oder Shift-Ziehen für Auswahlrahmen</td>
<td><ActionVideo src="quick-reference/multiselect-blocks.mp4" alt="Mehrere Blöcke auswählen" /></td>
</tr>
<tr>
<td>Blöcke kopieren</td>
<td>`Mod+C` mit ausgewählten Blöcken</td>
<td rowSpan={2}><ActionVideo src="quick-reference/copy-paste.mp4" alt="Blöcke kopieren und einfügen" /></td>
</tr>
<tr>
<td>Blöcke einfügen</td>
<td>`Mod+V` zum Einfügen kopierter Blöcke</td>
</tr>
<tr>
<td>Blöcke duplizieren</td>
<td>Rechtsklick → **Duplizieren**</td>
<td><ActionVideo src="quick-reference/duplicate-block.mp4" alt="Blöcke duplizieren" /></td>
</tr>
<tr>
<td>Blöcke löschen</td>
<td>`Delete` oder `Backspace` Taste oder Rechtsklick → **Löschen**</td>
<td><ActionImage src="/static/quick-reference/delete-block.png" alt="Block löschen" /></td>
</tr>
<tr>
<td>Block umbenennen</td>
<td>Auf Blocknamen im Header klicken oder im Editor-Panel bearbeiten</td>
<td><ActionVideo src="quick-reference/rename-block.mp4" alt="Block umbenennen" /></td>
</tr>
<tr>
<td>Block aktivieren/deaktivieren</td>
<td>Rechtsklick → **Aktivieren/Deaktivieren**</td>
<td><ActionImage src="/static/quick-reference/disable-block.png" alt="Block deaktivieren" /></td>
</tr>
<tr>
<td>Block sperren/entsperren</td>
<td>Über Block hovern → Auf Schloss-Symbol klicken (nur Admin)</td>
<td><ActionImage src="/static/quick-reference/lock-block.png" alt="Block sperren" /></td>
</tr>
<tr>
<td>Handle-Ausrichtung umschalten</td>
<td>Rechtsklick → **Handles umschalten**</td>
<td><ActionVideo src="quick-reference/toggle-handles.mp4" alt="Handle-Ausrichtung umschalten" /></td>
</tr>
<tr>
<td>Block konfigurieren</td>
<td>Block auswählen → Editor-Panel rechts verwenden</td>
<td><ActionVideo src="quick-reference/configure-block.mp4" alt="Block konfigurieren" /></td>
</tr>
</tbody>
</table>
## Verbindungen
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Verbindung erstellen</td>
<td>Vom Ausgangs-Handle zum Eingangs-Handle ziehen</td>
<td><ActionVideo src="quick-reference/connect-blocks.mp4" alt="Blöcke verbinden" /></td>
</tr>
<tr>
<td>Verbindung löschen</td>
<td>Auf Kante klicken zum Auswählen → `Delete` Taste</td>
<td><ActionVideo src="quick-reference/delete-connection.mp4" alt="Verbindung löschen" /></td>
</tr>
<tr>
<td>Ausgabe in anderem Block verwenden</td>
<td>Verbindungs-Tag in Eingabefeld ziehen</td>
<td><ActionVideo src="quick-reference/connection-tag.mp4" alt="Verbindungs-Tag verwenden" /></td>
</tr>
</tbody>
</table>
## Panels und Ansichten
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Symbolleiste durchsuchen</td>
<td>`Mod+F`</td>
<td><ActionVideo src="quick-reference/search-toolbar.mp4" alt="Symbolleiste durchsuchen" /></td>
</tr>
<tr>
<td>Alles durchsuchen</td>
<td>`Mod+K`</td>
<td><ActionImage src="/static/quick-reference/search-everything.png" alt="Alles durchsuchen" /></td>
</tr>
<tr>
<td>Manuellen Modus umschalten</td>
<td>Klicken Sie auf die Umschalt-Schaltfläche, um zwischen manuell und Selektor zu wechseln</td>
<td><ActionImage src="/static/quick-reference/toggle-manual-mode.png" alt="Manuellen Modus umschalten" /></td>
</tr>
<tr>
<td>Seitenleiste ein-/ausklappen</td>
<td>Klicken Sie auf die Einklappen-Schaltfläche in der Seitenleiste</td>
<td><ActionVideo src="quick-reference/collapse-sidebar.mp4" alt="Seitenleiste einklappen" /></td>
</tr>
</tbody>
</table>
## Ausführen und Testen
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Workflow ausführen</td>
<td>Klicken Sie auf die Schaltfläche Workflow ausführen oder `Mod+Enter`</td>
<td><ActionImage src="/static/quick-reference/run-workflow.png" alt="Workflow ausführen" /></td>
</tr>
<tr>
<td>Workflow stoppen</td>
<td>Klicken Sie auf die Stopp-Schaltfläche oder `Mod+Enter` während der Ausführung</td>
<td><ActionImage src="/static/quick-reference/stop-workflow.png" alt="Workflow stoppen" /></td>
</tr>
<tr>
<td>Mit Chat testen</td>
<td>Verwenden Sie das Chat-Panel auf der rechten Seite</td>
<td><ActionImage src="/static/quick-reference/test-chat.png" alt="Mit Chat testen" /></td>
</tr>
<tr>
<td>Ausgabe zum Anzeigen auswählen</td>
<td>Klicken Sie auf das Dropdown-Menü im Chat-Panel → Wählen Sie Block-Ausgabe aus</td>
<td><ActionImage src="/static/quick-reference/output-select.png" alt="Ausgabe zum Anzeigen auswählen" /></td>
</tr>
<tr>
<td>Chat-Verlauf löschen</td>
<td>Klicken Sie auf die Löschen-Schaltfläche im Chat-Panel</td>
<td><ActionImage src="/static/quick-reference/clear-chat.png" alt="Chat-Verlauf löschen" /></td>
</tr>
<tr>
<td>Ab Block ausführen</td>
<td>Bewegen Sie den Mauszeiger über den Block → Klicken Sie auf die Wiedergabe-Schaltfläche oder Rechtsklick → **Ab Block ausführen**</td>
<td><ActionImage src="/static/quick-reference/run-from-block.png" alt="Ab Block ausführen" /></td>
</tr>
<tr>
<td>Bis Block ausführen</td>
<td>Rechtsklick auf Block → **Bis Block ausführen**</td>
<td><ActionImage src="/static/quick-reference/run-until-block.png" alt="Bis Block ausführen" /></td>
</tr>
<tr>
<td>Ausführungsprotokolle anzeigen</td>
<td>Öffnen Sie das Terminal-Panel unten oder `Mod+L`</td>
<td><ActionImage src="/static/quick-reference/terminal.png" alt="Terminal für Ausführungsprotokolle" /></td>
</tr>
<tr>
<td>Protokolle filtern</td>
<td>Klicken Sie auf das Filter-Symbol im Terminal → Filtern Sie nach Block oder Status</td>
<td><ActionImage src="/static/quick-reference/filter-block.png" alt="Protokolle nach Block filtern" /></td>
</tr>
<tr>
<td>Protokolle durchsuchen</td>
<td>Verwenden Sie das Suchfeld im Terminal oder Rechtsklick auf Protokolleintrag → **Suchen**</td>
<td><ActionImage src="/static/quick-reference/terminal-search.png" alt="Protokolle durchsuchen" /></td>
</tr>
<tr>
<td>Protokolleintrag kopieren</td>
<td>Zwischenablage-Symbol oder Rechtsklick auf Protokolleintrag → **Kopieren**</td>
<td><ActionImage src="/static/quick-reference/copy-log.png" alt="Protokolleintrag kopieren" /></td>
</tr>
<tr>
<td>Terminal leeren</td>
<td>Papierkorb-Symbol oder `Mod+D`</td>
<td><ActionImage src="/static/quick-reference/clear-terminal.png" alt="Terminal leeren" /></td>
</tr>
</tbody>
</table>
## Bereitstellung
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Workflow bereitstellen</td>
<td>Klicken Sie auf die Schaltfläche **Bereitstellen** im Panel</td>
<td><ActionImage src="/static/quick-reference/deploy.png" alt="Workflow bereitstellen" /></td>
</tr>
<tr>
<td>Bereitstellung aktualisieren</td>
<td>Klicken Sie auf **Aktualisieren**, wenn Änderungen erkannt werden</td>
<td><ActionImage src="/static/quick-reference/update-deployment.png" alt="Bereitstellung aktualisieren" /></td>
</tr>
<tr>
<td>Bereitstellungsstatus anzeigen</td>
<td>Überprüfen Sie die Statusanzeige (Live/Aktualisieren/Bereitstellen) im Tab „Bereitstellen"</td>
<td><ActionImage src="/static/quick-reference/view-deployment.png" alt="Bereitstellungsstatus anzeigen" /></td>
</tr>
<tr>
<td>Bereitstellung zurücksetzen</td>
<td>Greifen Sie auf frühere Versionen im Tab „Bereitstellen" zu → **Zu Live befördern**</td>
<td><ActionImage src="/static/quick-reference/promote-deployment.png" alt="Bereitstellung zu Live befördern" /></td>
</tr>
<tr>
<td>Versionsbeschreibung hinzufügen</td>
<td>Tab „Bereitstellen" → Klicken Sie auf das Beschreibungssymbol → Beschreibung hinzufügen oder generieren</td>
<td><ActionVideo src="quick-reference/deployment-description.mp4" alt="Versionsbeschreibung für Bereitstellung hinzufügen" /></td>
</tr>
<tr>
<td>API-Endpunkt kopieren</td>
<td>Tab „Bereitstellen" → API → API-cURL kopieren</td>
<td><ActionImage src="/static/quick-reference/copy-api.png" alt="API-Endpunkt kopieren" /></td>
</tr>
</tbody>
</table>
## Variablen
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Workflow-Variable hinzufügen / bearbeiten / löschen</td>
<td>Panel → Variablen → **Variable hinzufügen**, zum Bearbeiten klicken oder Löschsymbol verwenden</td>
<td><ActionImage src="/static/quick-reference/variables.png" alt="Variablen-Panel" /></td>
</tr>
<tr>
<td>Umgebungsvariable hinzufügen</td>
<td>Einstellungen → **Umgebungsvariablen** → **Hinzufügen**</td>
<td><ActionImage src="/static/quick-reference/add-env-variable.png" alt="Umgebungsvariable hinzufügen" /></td>
</tr>
<tr>
<td>Auf Workflow-Variable verweisen</td>
<td>Verwenden Sie die Syntax `<blockName.itemName>` in Block-Eingaben</td>
<td><ActionImage src="/static/quick-reference/variable-reference.png" alt="Auf Workflow-Variable verweisen" /></td>
</tr>
<tr>
<td>Auf Umgebungsvariable verweisen</td>
<td>Verwenden Sie die Syntax `{{ENV_VAR}}` in Block-Eingaben</td>
<td><ActionImage src="/static/quick-reference/env-variable-reference.png" alt="Auf Umgebungsvariable verweisen" /></td>
</tr>
</tbody>
</table>

View File

@@ -7,10 +7,10 @@ import { Card, Cards } from 'fumadocs-ui/components/card'
import { Step, Steps } from 'fumadocs-ui/components/steps'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
Das offizielle Python SDK für Sim ermöglicht es Ihnen, Workflows programmatisch aus Ihren Python-Anwendungen mithilfe des offiziellen Python SDKs auszuführen.
Das offizielle Python SDK für Sim ermöglicht es Ihnen, Workflows programmatisch aus Ihren Python-Anwendungen heraus mit dem offiziellen Python SDK auszuführen.
<Callout type="info">
Das Python SDK unterstützt Python 3.8+ mit asynchroner Ausführungsunterstützung, automatischer Ratenbegrenzung mit exponentiellem Backoff und Nutzungsverfolgung.
Das Python SDK unterstützt Python 3.8+ mit Unterstützung für asynchrone Ausführung, automatischer Ratenbegrenzung mit exponentiellem Backoff und Nutzungsverfolgung.
</Callout>
## Installation
@@ -75,16 +75,16 @@ result = client.execute_workflow(
- `input_data` (dict, optional): Eingabedaten, die an den Workflow übergeben werden
- `timeout` (float, optional): Timeout in Sekunden (Standard: 30.0)
- `stream` (bool, optional): Streaming-Antworten aktivieren (Standard: False)
- `selected_outputs` (list[str], optional): Block-Ausgaben, die im `blockName.attribute`Format gestreamt werden sollen (z.B. `["agent1.content"]`)
- `selected_outputs` (list[str], optional): Block-Ausgaben zum Streamen im Format `blockName.attribute` (z. B. `["agent1.content"]`)
- `async_execution` (bool, optional): Asynchron ausführen (Standard: False)
**Rückgabe:** `WorkflowExecutionResult | AsyncExecutionResult`
**Rückgabewert:** `WorkflowExecutionResult | AsyncExecutionResult`
Wenn `async_execution=True`, wird sofort mit einer Task-ID zum Abfragen zurückgegeben. Andernfalls wird auf den Abschluss gewartet.
Wenn `async_execution=True`, wird sofort mit einer Task-ID zum Polling zurückgegeben. Andernfalls wird auf die Fertigstellung gewartet.
##### get_workflow_status()
Den Status eines Workflows abrufen (Bereitstellungsstatus usw.).
Ruft den Status eines Workflows ab (Deployment-Status usw.).
```python
status = client.get_workflow_status("workflow-id")
@@ -98,7 +98,7 @@ print("Is deployed:", status.is_deployed)
##### validate_workflow()
Überprüfen, ob ein Workflow für die Ausführung bereit ist.
Überprüft, ob ein Workflow zur Ausführung bereit ist.
```python
is_ready = client.validate_workflow("workflow-id")
@@ -114,7 +114,7 @@ if is_ready:
##### get_job_status()
Den Status einer asynchronen Job-Ausführung abrufen.
Ruft den Status einer asynchronen Job-Ausführung ab.
```python
status = client.get_job_status("task-id-from-async-execution")
@@ -131,7 +131,7 @@ if status["status"] == "completed":
**Antwortfelder:**
- `success` (bool): Ob die Anfrage erfolgreich war
- `taskId` (str): Die Task-ID
- `status` (str): Einer der Werte `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
- `status` (str): Einer von `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
- `metadata` (dict): Enthält `startedAt`, `completedAt` und `duration`
- `output` (any, optional): Die Workflow-Ausgabe (wenn abgeschlossen)
- `error` (any, optional): Fehlerdetails (wenn fehlgeschlagen)
@@ -139,7 +139,7 @@ if status["status"] == "completed":
##### execute_with_retry()
Einen Workflow mit automatischer Wiederholung bei Ratenbegrenzungsfehlern unter Verwendung von exponentiellem Backoff ausführen.
Führt einen Workflow mit automatischer Wiederholung bei Rate-Limit-Fehlern unter Verwendung von exponentiellem Backoff aus.
```python
result = client.execute_with_retry(
@@ -161,13 +161,13 @@ result = client.execute_with_retry(
- `selected_outputs` (list, optional): Block-Ausgaben zum Streamen
- `async_execution` (bool, optional): Asynchron ausführen
- `max_retries` (int, optional): Maximale Anzahl von Wiederholungen (Standard: 3)
- `initial_delay` (float, optional): Anfängliche Verzögerung in Sekunden (Standard: 1.0)
- `initial_delay` (float, optional): Anfangsverzögerung in Sekunden (Standard: 1.0)
- `max_delay` (float, optional): Maximale Verzögerung in Sekunden (Standard: 30.0)
- `backoff_multiplier` (float, optional): Backoff-Multiplikator (Standard: 2.0)
**Rückgabewert:** `WorkflowExecutionResult | AsyncExecutionResult`
**Rückgabe:** `WorkflowExecutionResult | AsyncExecutionResult`
Die Wiederholungslogik verwendet exponentielles Backoff (1s → 2s → 4s → 8s...) mit ±25% Jitter, um den Thundering-Herd-Effekt zu vermeiden. Wenn die API einen `retry-after` Header bereitstellt, wird dieser stattdessen verwendet.
Die Wiederholungslogik verwendet exponentielles Backoff (1s → 2s → 4s → 8s...) mit ±25% Jitter, um Thundering Herd zu verhindern. Wenn die API einen `retry-after`-Header bereitstellt, wird dieser stattdessen verwendet.
##### get_rate_limit_info()
@@ -185,7 +185,7 @@ if rate_limit_info:
##### get_usage_limits()
Ruft aktuelle Nutzungslimits und Kontingentinformationen für dein Konto ab.
Ruft aktuelle Nutzungslimits und Kontingentinformationen für Ihr Konto ab.
```python
limits = client.get_usage_limits()
@@ -320,9 +320,9 @@ class SimStudioError(Exception):
**Häufige Fehlercodes:**
- `UNAUTHORIZED`: Ungültiger API-Schlüssel
- `TIMEOUT`: Zeitüberschreitung bei der Anfrage
- `RATE_LIMIT_EXCEEDED`: Ratengrenze überschritten
- `USAGE_LIMIT_EXCEEDED`: Nutzungsgrenze überschritten
- `TIMEOUT`: Zeitüberschreitung der Anfrage
- `RATE_LIMIT_EXCEEDED`: Ratenlimit überschritten
- `USAGE_LIMIT_EXCEEDED`: Nutzungslimit überschritten
- `EXECUTION_ERROR`: Workflow-Ausführung fehlgeschlagen
## Beispiele
@@ -334,7 +334,7 @@ class SimStudioError(Exception):
Richten Sie den SimStudioClient mit Ihrem API-Schlüssel ein.
</Step>
<Step title="Workflow validieren">
Prüfen Sie, ob der Workflow bereitgestellt und für die Ausführung bereit ist.
Prüfen Sie, ob der Workflow bereitgestellt und zur Ausführung bereit ist.
</Step>
<Step title="Workflow ausführen">
Führen Sie den Workflow mit Ihren Eingabedaten aus.
@@ -386,7 +386,7 @@ Behandeln Sie verschiedene Fehlertypen, die während der Workflow-Ausführung au
from simstudio import SimStudioClient, SimStudioError
import os
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_error_handling():
try:
@@ -409,11 +409,20 @@ def execute_with_error_handling():
raise
```
### Verwendung des Kontextmanagers
### Verwendung des Context-Managers
Verwenden Sie den Client als Kontextmanager, um die Ressourcenbereinigung automatisch zu handhaben:
Verwenden Sie den Client als Context-Manager, um die Ressourcenbereinigung automatisch zu handhaben:
---CODE-PLACEHOLDER-ef99d3dd509e04865d5b6b0e0e03d3f8---
```python
from simstudio import SimStudioClient
import os
# Using context manager to automatically close the session
with SimStudioClient(api_key=os.getenv("SIM_API_KEY")) as client:
result = client.execute_workflow("workflow-id")
print("Result:", result)
# Session is automatically closed here
```
### Batch-Workflow-Ausführung
@@ -466,7 +475,7 @@ for result in results:
### Asynchrone Workflow-Ausführung
Führen Sie Workflows asynchron für lang laufende Aufgaben aus:
Führen Sie Workflows asynchron für langwierige Aufgaben aus:
```python
import os
@@ -510,9 +519,9 @@ def execute_async():
execute_async()
```
### Rate-Limiting und Wiederholungsversuche
### Ratenlimitierung und Wiederholung
Behandle Rate-Limits automatisch mit exponentiellem Backoff:
Behandeln Sie Ratenbegrenzungen automatisch mit exponentiellem Backoff:
```python
import os
@@ -549,7 +558,7 @@ execute_with_retry_handling()
### Nutzungsüberwachung
Überwache deine Kontonutzung und -limits:
Überwachen Sie die Nutzung und Limits Ihres Kontos:
```python
import os
@@ -593,13 +602,13 @@ check_usage()
### Streaming-Workflow-Ausführung
Führe Workflows mit Echtzeit-Streaming-Antworten aus:
Führen Sie Workflows mit Echtzeit-Streaming-Antworten aus:
```python
from simstudio import SimStudioClient
import os
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_streaming():
"""Execute workflow with streaming enabled."""
@@ -619,7 +628,7 @@ def execute_with_streaming():
execute_with_streaming()
```
Die Streaming-Antwort folgt dem Server-Sent Events (SSE) Format:
Die Streaming-Antwort folgt dem Server-Sent-Events- (SSE-) Format:
```
data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":"One"}
@@ -688,9 +697,9 @@ if __name__ == '__main__':
app.run(debug=True)
```
### Umgebungskonfiguration
### Umgebungs­konfiguration
Konfiguriere den Client mit Umgebungsvariablen:
Konfigurieren Sie den Client mit Umgebungsvariablen:
<Tabs items={['Development', 'Production']}>
<Tab value="Development">
@@ -727,27 +736,27 @@ Konfiguriere den Client mit Umgebungsvariablen:
</Tab>
</Tabs>
## API-Schlüssel erhalten
## Ihren API-Schlüssel erhalten
<Steps>
<Step title="Bei Sim anmelden">
Navigiere zu [Sim](https://sim.ai) und melde dich bei deinem Konto an.
Navigieren Sie zu [Sim](https://sim.ai) und melden Sie sich in Ihrem Konto an.
</Step>
<Step title="Öffne deinen Workflow">
Navigiere zu dem Workflow, den du programmatisch ausführen möchtest.
<Step title="Workflow öffnen">
Navigieren Sie zu dem Workflow, den Sie programmatisch ausführen möchten.
</Step>
<Step title="Deploye deinen Workflow">
Klicke auf "Deploy", um deinen Workflow zu deployen, falls dies noch nicht geschehen ist.
<Step title="Workflow bereitstellen">
Klicken Sie auf "Bereitstellen", um Ihren Workflow bereitzustellen, falls dies noch nicht geschehen ist.
</Step>
<Step title="Erstelle oder wähle einen API-Schlüssel">
Wähle während des Deployment-Prozesses einen API-Schlüssel aus oder erstelle einen neuen.
<Step title="API-Schlüssel erstellen oder auswählen">
Wählen oder erstellen Sie während des Bereitstellungsprozesses einen API-Schlüssel.
</Step>
<Step title="Kopiere den API-Schlüssel">
Kopiere den API-Schlüssel zur Verwendung in deiner Python-Anwendung.
<Step title="API-Schlüssel kopieren">
Kopieren Sie den API-Schlüssel, um ihn in Ihrer Python-Anwendung zu verwenden.
</Step>
</Steps>
## Anforderungen
## Voraussetzungen
- Python 3.8+
- requests >= 2.25.0

Some files were not shown because too many files have changed in this diff Show More