Skip to content

v0.6.31: elevenlabs voice, trigger.dev fixes, cloud whitelabeling for enterprises#4053

Merged
waleedlatif1 merged 9 commits intomainfrom
staging
Apr 8, 2026
Merged

v0.6.31: elevenlabs voice, trigger.dev fixes, cloud whitelabeling for enterprises#4053
waleedlatif1 merged 9 commits intomainfrom
staging

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

icecrasher321 and others added 8 commits April 8, 2026 01:01
* feat(speech): unified voice interface

* add metering for voice input usage

* ip key

* use shared getclientip helper, fix deployed chat

* cleanup code

* prep merge

* merge staging in

* add billing check

* add voice input section

* remove skip billing

* address comments
* fix(kb): improve error logging when connector token resolution fails

The generic "Failed to obtain access token" error hid the actual root cause.
Now logs credentialId, userId, authMode, and provider to help diagnose
token refresh failures in trigger.dev.

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

* feat(kb): disable connectors after 10 consecutive sync failures

Connectors that fail 10 times in a row are set to 'disabled' status,
stopping the cron from scheduling further syncs. The UI shows an alert
triangle with a reconnect banner. Users can re-enable via the play
button or by reconnecting their account, which resets failures.

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

* fix(kb): disable sync button for disabled connectors, use amber badge variant

Sync button should be disabled when connector is in disabled state to
guide users toward reconnecting first. Badge variant changed from red
to amber to match the warning banner styling.

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

* fix(kb): address PR review comments for disabled connector feature

- Use `=== undefined` instead of falsy check for nextSyncAt to preserve
  explicit null (manual sync only) when syncIntervalMinutes is 0
- Gate Reconnect button on serviceId/providerId so it only renders for
  OAuth connectors; show appropriate copy for API key connectors

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

* fix(kb): move resolveAccessToken inside try/catch for circuit-breaker coverage

Token resolution failures (e.g. revoked OAuth tokens) were thrown before
the try/catch block, bypassing consecutiveFailures tracking entirely.
Also removes dead `if (refreshed)` guards at mid-sync refresh sites since
resolveAccessToken now always returns a string or throws.

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

* fix(kb): remove dead interval branch when re-enabling connector

When `updates.nextSyncAt === undefined`, syncIntervalMinutes was not in
the request, so `parsed.data.syncIntervalMinutes` is always undefined.
Simplify to just schedule an immediate sync — the sync engine sets the
proper nextSyncAt based on the connector's DB interval after completion.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…m cross-block (#4045)

* fix(parallel): remove broken node-counting completion in parallel blocks

* fix resolver claim

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Theodore Li <theo@sim.ai>
* fix(webhook): throw webhook errors as 4xxs

* Fix shadowing body var

---------

Co-authored-by: Theodore Li <theo@sim.ai>
* feat(enterprise): cloud whitelabeling for enterprise orgs

* fix(enterprise): scope enterprise plan check to target org in whitelabel PUT

* fix(enterprise): use isOrganizationOnEnterprisePlan for org-scoped enterprise check

* fix(enterprise): allow clearing whitelabel fields and guard against empty update result

* fix(enterprise): remove webp from logo accept attribute to match upload hook validation

* improvement(billing): use isBillingEnabled instead of isProd for plan gate bypasses

* fix(enterprise): show whitelabeling nav item when billing is enabled on non-hosted environments

* fix(enterprise): accept relative paths for logoUrl since upload API returns /api/files/serve/ paths

* fix(whitelabeling): prevent logo flash on refresh by hiding logo while branding loads

* fix(whitelabeling): wire hover color through CSS token on tertiary buttons

* fix(whitelabeling): show sim logo by default, only replace when org logo loads

* fix(whitelabeling): cache org logo url in localstorage to eliminate flash on repeat visits

* feat(whitelabeling): add wordmark support with drag/drop upload

* updated turbo

* fix(whitelabeling): defer localstorage read to effect to prevent hydration mismatch

* fix(whitelabeling): use layout effect for cache read to eliminate logo flash before paint

* fix(whitelabeling): cache theme css to eliminate color flash before org settings resolve

* fix(whitelabeling): deduplicate HEX_COLOR_REGEX into lib/branding and remove mutation from useCallback deps

* fix(whitelabeling): use cookie-based SSR cache to eliminate brand flash on all page loads

* fix(whitelabeling): use !orgSettings condition to fix SSR brand cache injection

React Query returns isLoading: false with data: undefined during SSR, so the
previous brandingLoading condition was always false on the server — initialCache
was never injected into brandConfig. Changing to !orgSettings correctly applies
the cookie cache both during SSR and while the client-side query loads, eliminating
the logo flash on hard refresh.
@cursor
Copy link
Copy Markdown

cursor bot commented Apr 8, 2026

PR Summary

High Risk
High risk because it adds new unauthenticated/public-facing API endpoints and billing/usage recording for voice sessions, introduces DB-backed enterprise whitelabeling, and changes workflow parallel execution aggregation semantics.

Overview
Adds ElevenLabs-based voice input end-to-end: new /api/settings/voice capability check and /api/speech/token token+billing endpoint (rate-limited, supports deployed chat auth), plus a new WebSocket/PCM streaming implementation in chat UIs and a shared useSpeechToText hook; CSP is updated to allow ElevenLabs connections and voice usage is logged as voice-input.

Introduces enterprise org whitelabeling backed by a new organization.whitelabel_settings JSON column and GET/PUT /api/organizations/[id]/whitelabel (role + plan gated), with a new settings screen and a workspace BrandingProvider that caches logo/wordmark/theme CSS in a cookie for SSR-first branding.

Fixes and hardens several platform behaviors: knowledge connectors can become disabled after repeated sync failures (UI + API reset on re-activate), parallel execution no longer uses node-count completion and the parallel variable resolver can read result(s) outputs across blocks by name, IP extraction is centralized via getClientIp, webhook preprocessing now surfaces upstream 4xx/5xx details, socket-token generation logs errors, and Trigger.dev build includes react-dom/@react-email/render.

Reviewed by Cursor Bugbot for commit 1189400. Configure here.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Apr 8, 2026 7:51pm

Request Review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1189400. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 8, 2026

Greptile Summary

This release bundles eight changes: ElevenLabs WebSocket-based STT migration, cloud whitelabeling for enterprise orgs (new DB column, API, settings UI, theme injection), parallel-executor completion fix, webhook 4xx error propagation, KB connector disable-on-failure, and trigger.dev package additions.

  • P1 — Billing before mic permission (apps/sim/app/api/speech/token/route.ts): Usage is recorded and the ElevenLabs single-use token is consumed before the client requests microphone access. A mic denial silently wastes the token and bills the user.
  • P1 — 3-digit hex not expanded in inject-theme.ts: isDarkBackground skips hex expansion, producing NaN for the blue channel and an incorrect auto-contrast text color when an env-var brand color uses 3-digit hex notation (e.g. #fff). org-branding-utils.ts handles this correctly.

Confidence Score: 3/5

Mergeable with caution — two P1 correctness issues should be addressed before or shortly after shipping.

The parallel and webhook fixes are clean and well-tested. The whitelabeling API and UI are well-guarded. However, the STT billing-before-permission ordering means users who deny mic access are charged for sessions that never happen, and the 3-digit hex bug in inject-theme.ts can silently produce wrong button text colors for env-var-configured brand colors.

apps/sim/app/api/speech/token/route.ts and apps/sim/hooks/use-speech-to-text.ts (billing ordering), apps/sim/ee/whitelabeling/inject-theme.ts (3-digit hex handling)

Vulnerabilities

  • The new PUT /api/organizations/[id]/whitelabel endpoint correctly verifies org membership, owner/admin role, and enterprise plan before allowing mutations — no privilege escalation path observed.
  • logoUrl and wordmarkUrl are stored as-is without URL validation beyond z.string().min(1); server-side rendering of these values in <Image> components uses Next.js's unoptimized prop, limiting SSRF risk to client-side requests only.
  • No secrets, credentials, or private keys are exposed in new code paths.
  • No SQL injection risk: all DB queries use Drizzle ORM parameterised queries.

Important Files Changed

Filename Overview
apps/sim/app/api/speech/token/route.ts New ElevenLabs STT token endpoint with rate limiting and billing — records usage before mic permission is confirmed on the client, causing potential over-billing on mic denial.
apps/sim/hooks/use-speech-to-text.ts Migrates STT from browser Web Speech API to ElevenLabs WebSocket streaming; uses deprecated createScriptProcessor and fetches token (triggering billing) before mic permission is obtained.
apps/sim/ee/whitelabeling/inject-theme.ts Generates CSS variable overrides from env-var brand config; isDarkBackground does not expand 3-digit hex colors before parsing, producing wrong luminance and incorrect contrast text color.
apps/sim/app/api/organizations/[id]/whitelabel/route.ts Well-structured GET/PUT endpoint for org whitelabel settings; validates membership, role (owner/admin), and enterprise plan before allowing mutations.
apps/sim/ee/whitelabeling/components/whitelabeling-settings.tsx New settings UI for org whitelabeling; initializes form state in the render body (not useEffect) which is an anti-pattern but non-critical; otherwise well-structured with proper billing guards.
apps/sim/executor/orchestrators/parallel.ts Removes broken node-count-based completion detection in favour of sentinel-end-node edge mechanism; simplifies aggregation and fixes cross-block resolver claims.
apps/sim/lib/webhooks/providers/generic.ts Adds custom success response formatting (status code + body) for generic webhook provider; correctly validates status code range.
apps/sim/ee/whitelabeling/hooks/whitelabel.ts React Query hooks for fetching/updating whitelabel settings; follows project conventions with explicit staleTime, signal forwarding, and onSettled for cache invalidation.
apps/sim/app/_styles/globals.css Adds default CSS variable values for auth button colors needed by the whitelabeling theme system; modifying globals.css violates project convention but is technically necessary for root-level CSS defaults.
packages/db/schema.ts Adds whitelabelSettings JSONB column to the organization table for storing per-org branding configuration.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant C as Client (useSpeechToText)
    participant ST as /api/speech/token
    participant EL as ElevenLabs API
    participant B as Billing

    U->>C: toggleListening()
    C->>ST: POST /api/speech/token
    ST->>EL: POST /v1/single-use-token/realtime_scribe
    EL-->>ST: { token }
    ST->>B: recordUsage (full session cost)
    ST-->>C: { token }
    C->>C: getUserMedia() — mic permission dialog
    alt Mic Granted
        C->>EL: WebSocket connect (token)
        EL-->>C: partial_transcript / committed_transcript
        C->>U: onTranscript(text)
    else Mic Denied
        C->>C: cleanup() — token and billing already consumed
        note right of C: User billed, no STT used
    end
Loading

Comments Outside Diff (2)

  1. apps/sim/ee/whitelabeling/inject-theme.ts, line 6-9 (link)

    P1 3-digit hex colors produce incorrect luminance and wrong button text color

    isDarkBackground in this file assumes a 6-digit hex string. For a 3-digit value like #abc, parsing gives wrong channel values (r=0xab, g=0xc, b=NaN), so the luminance check is unreliable and the auto-contrasted button text color may be incorrect.

    org-branding-utils.ts (the equivalent function used for DB-sourced settings) correctly expands 3-digit hex before parsing. HEX_COLOR_REGEX (used for validation) explicitly allows 3-digit values, so an environment variable like #fff is accepted but handled incorrectly here. Additionally, substr is deprecated — org-branding-utils.ts already uses slice consistently.

  2. apps/sim/app/_styles/globals.css, line 18-23 (link)

    P2 Global stylesheet modified for whitelabel CSS variables

    Per project conventions, edits to globals.css should be avoided in favour of local component styles. The new --auth-primary-btn-* CSS variables are used as default fallbacks for the whitelabeling theme system and are overridden by inject-theme.ts / generateOrgThemeCSS. Since these are true :root-level defaults that must be in place before any component renders, the globals.css placement is justifiable — but it's worth documenting the necessity in a comment so future contributors know not to remove them.

    Rule Used: Avoid editing the globals.css file unless absolute... (source)

    Learnt From
    simstudioai/sim#367

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Reviews (1): Last reviewed commit: "feat(enterprise): cloud whitelabeling fo..." | Re-trigger Greptile

Comment on lines +146 to +165
if (billingUserId) {
const maxMinutes = chatId ? CHAT_SESSION_MAX_MINUTES : WORKSPACE_SESSION_MAX_MINUTES
const sessionCost = VOICE_SESSION_COST_PER_MIN * maxMinutes

await recordUsage({
userId: billingUserId,
entries: [
{
category: 'fixed',
source: 'voice-input',
description: `Voice input session (${maxMinutes} min)`,
cost: sessionCost * getCostMultiplier(),
},
],
}).catch((err) => {
logger.warn('Failed to record voice input usage, continuing:', err)
})
}

return NextResponse.json({ token: data.token })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Usage billed before microphone permission is confirmed

The server records usage (line 150) and issues the ElevenLabs token before the client has checked or obtained microphone access. In use-speech-to-text.ts, getUserMedia is called only after the token response is received. If the user denies microphone permission, NotAllowedError is thrown, cleanup runs, and the session is abandoned — but billing was already recorded and the single-use token was consumed.

Consider requesting getUserMedia on the client before calling /api/speech/token, or deferring usage recording to after the WebSocket connection is successfully established.

Comment on lines +265 to +266
const processor = audioContext.createScriptProcessor(4096, 1, 1)
processorRef.current = processor
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 createScriptProcessor is deprecated

AudioContext.createScriptProcessor() was deprecated in the Web Audio API spec in favour of AudioWorklet. It still functions in most browsers but emits deprecation warnings and is expected to be removed eventually. Migrating to an AudioWorkletProcessor would future-proof this and also move audio processing off the main thread.

Comment on lines +165 to +179
if (savedSettings && !formInitialized) {
setBrandName(savedSettings.brandName ?? '')
setPrimaryColor(savedSettings.primaryColor ?? '')
setPrimaryHoverColor(savedSettings.primaryHoverColor ?? '')
setAccentColor(savedSettings.accentColor ?? '')
setAccentHoverColor(savedSettings.accentHoverColor ?? '')
setSupportEmail(savedSettings.supportEmail ?? '')
setDocumentationUrl(savedSettings.documentationUrl ?? '')
setTermsUrl(savedSettings.termsUrl ?? '')
setPrivacyUrl(savedSettings.privacyUrl ?? '')
setHidePoweredBySim(savedSettings.hidePoweredBySim ?? false)
setLogoUrl(savedSettings.logoUrl ?? null)
setWordmarkUrl(savedSettings.wordmarkUrl ?? null)
setFormInitialized(true)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 State updates during render body are a React anti-pattern

Calling multiple setState functions synchronously inside the component render body (rather than inside a useEffect) causes React to schedule an extra render immediately after the current one. In Strict Mode this compounds further. The formInitialized guard prevents infinite loops, but the idiomatic fix is a useEffect that depends on savedSettings:

useEffect(() => {
  if (savedSettings && !formInitialized) {
    setBrandName(savedSettings.brandName ?? '')
    // ... rest of initialisation
    setFormInitialized(true)
  }
}, [savedSettings, formInitialized])

@waleedlatif1 waleedlatif1 merged commit cf233bb into main Apr 8, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants