* fix(aws): add validateAwsRegion to all AWS route schemas to prevent SSRF
* fix(validation): add mx and eu-isoe prefixes to validateAwsRegion regex
* test(validation): add mx-central-1, eu-isoe-west-1, and us-iso-west-1 region test cases
* fix(aws): eliminate double validateAwsRegion call and fix regex alternation order
- Replace double-call .refine() pattern with single-call + static message across all 61 AWS routes
- Reorder regex alternation to put longer prefixes first (eu-isoe before eu, us-isob/us-iso/us-gov before us) for engine-agnostic correctness
* improvement(contact): add Turnstile CAPTCHA, honeypot, and robustness fixes
- Add Cloudflare Turnstile with graceful degradation: when the widget
fails to load (ad blockers, iOS privacy, corporate DNS), submissions
fall through to a tighter rate-limit bucket rather than hard-blocking
- Add honeypot field to filter automated submissions without user impact
- Add separate CAPTCHA_UNAVAILABLE_RATE_LIMIT bucket (3/min) for the
no-captcha path so spam via ad-blocker bypass remains expensive
- Pass expectedHostname to verifyTurnstileToken to close cross-site
token reuse gap
- Add SITE_HOSTNAME as module-level constant (avoid URL parsing per req)
- Wire onExpire/onError/onUnsupported callbacks so token expiry during
slow form-filling falls back gracefully instead of showing a captcha error
- Add getResponsePromise(30_000) timeout to prevent indefinite hang on
network blips
- Add size: 'invisible' to Turnstile options (required for execute mode)
- Move turnstile.ts to lib/core/security/ alongside csp/encryption/input-validation
- Switch all CSS to --landing-* variables throughout contact form
- Move error display inline next to label with truncation in LandingField
- Add labelClassName prop to LandingField for context-specific overrides
- Simplify contact page to single-column max-w-[640px] layout
* fix(contact): fall through to no-captcha rate limit on Cloudflare transport errors
* chore(contact): remove extraneous comments from route
* fix(contact): remove forced min-height on success state, let content flow naturally
* fix(contact): cast CONTACT_TOPIC_OPTIONS to satisfy Combobox mutable type
* fix(contact): disable submit during CAPTCHA resolution window, add relative to form
* feat(integrations): add AWS SES, IAM Identity Center, and enhanced IAM/STS/CloudWatch/DynamoDB integrations
- Add AWS SES v2 integration with 9 operations (send email, templated, bulk, templates, account)
- Add AWS IAM Identity Center integration with 12 operations (account assignments, permission sets, users, groups)
- Add 3 new IAM tools: list-attached-role-policies, list-attached-user-policies, simulate-principal-policy
- Fix DynamoDB duplicate subBlock IDs, add operation-scoped field names, add subblock migrations
- Add authMode: AuthMode.ApiKey to DynamoDB block
- Fix CloudWatch routes: toError, client.destroy(), withRouteHandler, auth outside try
- Fix STS/DynamoDB/IAM routes: nullable Zod schemas, withRouteHandler adoption
- Fix Identity Center: list_instances pagination, list_groups instanceArn condition
- Add subblock migrations for renamed DynamoDB fields (key, filterExpression, etc.)
- Apply withRouteHandler to all new and existing AWS tool routes
* docs(ses): add manual intro section to SES docs
* fix(dynamodb): add legacy fallbacks in params for subblock migration compatibility
Workflows saved with the old shared IDs (key, filterExpression, etc.) that migrate
to get-scoped slots via subblock-migrations still work correctly on update/delete/scan/put
operations via fallback lookups in tools.config.params.
* feat(contact): add contact page, migrate help/demo forms to useMutation (#4242)
* feat(contact): add contact page, migrate help/demo forms to useMutation
* improvement(contact): address greptile review feedback
- Map contact topic to help email type for accurate confirmation emails
- Drop Zod schema details from 400 response on public /api/contact
- Wire aria-describedby + aria-invalid in LandingField for both forms
- Reset helpMutation on modal reopen to match demo-request pattern
* improvement(landing): extract shared LandingField component
* fix(landing): resolve error-page crash on invalid /models and /integrations routes (#4243)
* fix(layout): use plain inline script for PublicEnvScript to set env before chunks eval on error pages
* fix(landing): handle runtime env race on error-page renders
React skips SSR on unhandled server errors and re-renders on the client
(see vercel/next.js#63980, #82456). Root-layout scripts — including the
runtime env script that populates window.__ENV — are inserted but not
executed on that client re-render, so any client module that reads env
at module evaluation crashes the render into a blank "Application error"
overlay instead of rendering the styled 404.
This replaces the earlier PublicEnvScript tweak with the architectural
fix:
- auth-client.ts: fall back to window.location.origin when getBaseUrl()
throws on the client. Auth endpoints are same-origin, so this is the
correct baseURL on the client. Server-side we still throw on genuine
misconfig.
- loading.tsx under /models/[provider], /models/[provider]/[model], and
/integrations/[slug]: establishes a Suspense boundary below the root
layout so a page-level notFound() no longer invalidates the layout's
SSR output (the fix endorsed by Next.js maintainers in #63980).
- layout.tsx: revert disableNextScript — the research showed this
doesn't actually fix error-page renders. The real fix is above.
* improvement(landing): use emcn Loader in scoped loading.tsx, trim auth-client comment
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(iam): correct MissingContextValues mapping in simulatePrincipalPolicy
* fix(aws): add conditionExpression migration fallback for DynamoDB delete, fix SES pageSize min
* fix(aws): deep validation fixes across SES, IAM, Identity Center, DynamoDB integrations
- IAM: replace non-existent StatementId with SourcePolicyType in simulatePrincipalPolicy
- IAM: add .int() constraint to list-users/roles/policies/groups Zod schemas
- IAM: remove redundant manual requestId from all 21 IAM route handlers
- SES: add .refine() body validation to create-template route
- SES: make bulk email destination templateData optional, only include ReplacementEmailContent when present
- SES: fix pageSize guard to if (pageSize != null) to correctly forward 0
- SES: add max(100) to list-templates pageSize, revert list-identities to min(0) per SDK
- STS: fix logger.error calls to use structured metadata pattern
- Identity Center: remove deprecated account.Status fallback, use account.State only
- DynamoDB: convert empty interface extends to type aliases, remove redundant error field, fix barrel to absolute imports
* regen docs
* fix(iam): add .int() constraint to maxSessionDuration in create-role route
* fix(ses): forward pageSize=0 correctly in listIdentities util
* fix(aws): add gradient background to IdentityCenterIcon, fix listTemplates pageSize guard
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* improvement(landing): scope navbar/footer shell to (shell) route group, align scoped 404s with root
Move integrations and models page routes into a `(shell)` route group so the Navbar+Footer layout wraps pages but not `not-found.tsx`. This lets scoped 404s render the same `<AuthBackground>` + Navbar treatment as the root `/` 404, instead of appearing inside the landing CTA footer.
Extract the shared 404 markup into `<NotFoundView>` so root, integrations, and models 404s share a single source of truth. Route URLs are unchanged — route groups are URL-transparent.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(landing): convert relative imports to absolute in integrations (shell) page
Build failed because the move into the (shell) route group invalidated relative `./components/...` and `./data/...` imports. CLAUDE.md mandates absolute imports throughout — switching these resolves the Turbopack build errors.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(layout): use plain inline script for PublicEnvScript to set env before chunks eval on error pages
* fix(landing): handle runtime env race on error-page renders
React skips SSR on unhandled server errors and re-renders on the client
(see vercel/next.js#63980, #82456). Root-layout scripts — including the
runtime env script that populates window.__ENV — are inserted but not
executed on that client re-render, so any client module that reads env
at module evaluation crashes the render into a blank "Application error"
overlay instead of rendering the styled 404.
This replaces the earlier PublicEnvScript tweak with the architectural
fix:
- auth-client.ts: fall back to window.location.origin when getBaseUrl()
throws on the client. Auth endpoints are same-origin, so this is the
correct baseURL on the client. Server-side we still throw on genuine
misconfig.
- loading.tsx under /models/[provider], /models/[provider]/[model], and
/integrations/[slug]: establishes a Suspense boundary below the root
layout so a page-level notFound() no longer invalidates the layout's
SSR output (the fix endorsed by Next.js maintainers in #63980).
- layout.tsx: revert disableNextScript — the research showed this
doesn't actually fix error-page renders. The real fix is above.
* improvement(landing): use emcn Loader in scoped loading.tsx, trim auth-client comment
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat(contact): add contact page, migrate help/demo forms to useMutation
* improvement(contact): address greptile review feedback
- Map contact topic to help email type for accurate confirmation emails
- Drop Zod schema details from 400 response on public /api/contact
- Wire aria-describedby + aria-invalid in LandingField for both forms
- Reset helpMutation on modal reopen to match demo-request pattern
* improvement(landing): extract shared LandingField component
* improvement(enterprise): slack wizard UI, enterprise docs, data retention updates
* improvement(docs): add enterprise screenshots to sso, access-control, whitelabeling pages
* form
* fix(enterprise): address PR review — h-full for recently-deleted, shared SettingRow, toast UX, stale form fix, emcn tokens
* fix(whitelabeling): scope drop zone to thumbnail only, not full upload row
* fix(whitelabeling): remove drop image text from drag overlay
* fix(config): add DATA_RETENTION_ENABLED to env schema to fix build type error
* fix(testing): add isDataRetentionEnabled to feature flags mock
* improvement(docs): remove redundant requirements section from data-retention page
* improvement(docs): remove requirements sections from all enterprise doc pages
* improvement(docs): add screenshot to audit-logs page
* fix(data-retention): bypass enterprise gate when billing is disabled for self-hosted
* improvement(knowledge): show selector with saved option in connector edit modal
* fix(kb-connectors): clear canonical siblings when non-canonical dep changes; share selector field
* refactor(kb-connectors): extract canonical-field logic into useConnectorConfigFields hook
* fix(kb-connectors): only merge changed fields into sourceConfig on edit save
Avoids writing spurious empty-string keys for untouched optional fields when
another field triggers a save.
* refactor(kb-connectors): tighten state primitives in modals
- edit modal: replace useMemo([]) + eslint-disable with useState lazy
initializer for initialSourceConfig — same mount-once semantics
without the escape hatch.
- add modal: drop useCallback on handleConnectNewAccount (no observer
saw the reference) and inline the one call site.
* fix(billing): close TOCTOU race in subscription transfer, centralize stripe test mocks
* more mocks
* fix(testing): provide complete Stripe.Event defaults in createMockStripeEvent
* fix(testing): make dbChainMock .for('update') chainable with .limit()
* fix(billing): gate subscription transfer noop behind membership check
Previously the 'already belongs to this organization' early return fired
before the org/member lookups, letting any authenticated caller probe
sub-to-org pairings without being a member of the target org. Move the
noop check after the admin/owner verification so unauthorized callers
hit the 403 first.
* fix(workday): validate tenantUrl to prevent SSRF in SOAP client
* fix(workday): use validation.sanitized in buildWsdlUrl
* fix(security): enforce URL validation across connectors, providers, auth
- Azure OpenAI/Anthropic: validate user-supplied azureEndpoint with validateUrlWithDNS to block SSRF to private IPs, localhost (in hosted mode), and dangerous ports.
- ServiceNow connector: enforce ServiceNow domain allowlist via validateServiceNowInstanceUrl before calling the instance URL.
- Obsidian connector: validate vaultUrl with validateUrlWithDNS and reuse the resolved IP via secureFetchWithPinnedIPAndRetry to block DNS rebinding between validation and request.
- Signup + verify flows: pass redirect/callbackUrl/redirectAfter and stored inviteRedirectUrl through validateCallbackUrl; drop unsafe values and log a warning.
- lib/knowledge/documents/utils.ts: add secureFetchWithPinnedIPAndRetry wrapper around secureFetchWithPinnedIP (used by Obsidian).
* fix(obsidian): use isomorphic SSRF validation to unblock client build
The Obsidian connector is reachable from client bundles via `connectors/registry.ts` (the knowledge UI reads metadata like `.icon`/`.name`). Importing `validateUrlWithDNS` / `secureFetchWithPinnedIP` from `input-validation.server` pulled `dns/promises`, `http`, `https`, `net` into client chunks, breaking the Turbopack build:
Module not found: Can't resolve 'dns/promises'
./apps/sim/lib/core/security/input-validation.server.ts [Client Component Browser]
./apps/sim/connectors/obsidian/obsidian.ts [Client Component Browser]
./apps/sim/connectors/registry.ts [Client Component Browser]
Once that file polluted a browser context, Turbopack also failed to resolve the Node builtins in its legitimate server-route imports, cascading the error across App Routes and Server Components.
Fix: switch the Obsidian connector to the isomorphic `validateExternalUrl` + `fetchWithRetry` helpers, matching the pattern used by every other connector in the registry. This keeps the core SSRF protections:
- hosted Sim: blocks localhost, private IPs, HTTP (HTTPS enforced)
- self-hosted Sim: allows localhost + HTTP, still blocks non-loopback private IPs and dangerous ports (22, 25, 3306, 5432, 6379, 27017, 9200)
Drops the DNS-rebinding defense specifically (the IP-pinned fetch chain). The trade-off is acceptable because the vault URL is entered by the workspace admin — not arbitrary untrusted input — and hosted deployments already force the plugin to be exposed through a public URL (tunnel/port-forward), making rebinding a narrow threat.
Also reverts the `secureFetchWithPinnedIPAndRetry` wrapper in `lib/knowledge/documents/utils.ts` (no longer needed, and its `.server` import was the original source of the client-bundle pollution).
* fix(servicenow): propagate URL validation errors in getDocument
Match listDocuments behavior — invalid instance URL should surface as a
configuration error rather than being swallowed into a "document not found"
null response during sync.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(obsidian): drop allowHttp to restore HTTPS enforcement in hosted mode
allowHttp: true permitted plaintext HTTP for all hosts in all deployment
modes, contradicting the documented policy. The default validateExternalUrl
behavior already allows http://localhost in self-hosted mode (the actual
Obsidian Local REST API use case) via the built-in carve-out, while correctly
rejecting HTTP for public hosts in hosted mode — which prevents leaking the
Bearer access token over plaintext.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* improvement(codebase): migrate tests to dbChainMock, extract react-query hooks
Migrate 97 test files to centralized dbChainMock/dbChainMockFns helpers from
@sim/testing — removes hoisted chain-wiring boilerplate.
Extend dbChainMock to cover insert/update/delete/transaction/execute patterns.
Extract useGitHubStars and useVoiceSettings react-query hooks from inline fetches.
Centralize additional mocks (authMockFns, hybridAuthMockFns) and update docs.
* fix(github-stars): centralize fallback via initialData, remove stale constants
Move the placeholder star count into useGitHubStars as initialData with
initialDataUpdatedAt: 0 so `data` is always a narrowed string while still
refetching on mount. Fixes two Bugbot issues: stale '25.8k' in chat.tsx
(vs '27.8k' in navbar) and empty-string return in fetchGitHubStars that
bypassed `??` fallbacks in consumers.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(testing): wire dbChainMock.db to shared transaction and execute fns
dbChainMock.db.transaction was an inline vi.fn() separate from the exported
dbChainMockFns.transaction, so dbChainMockFns.transaction.mockResolvedValueOnce
and assertions silently targeted the wrong instance. dbChainMock.db also
omitted execute, so tests for any module that calls db.execute (logging-session,
table service, billing balance) would throw TypeError. Both mocks now reference
the module-level constants so overrides and resetDbChainMock affect the same fn.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(chat,testing): memoize welcome message and add selectDistinct to dbChainMock.db
Why:
- Welcome ChatMessage was rebuilt inline each render, producing a fresh
timestamp and new array identity — cascading to ChatMessageContainer
and VoiceInterface props on every tick.
- dbChainMockFns exports selectDistinct/selectDistinctOn but the
dbChainMock.db object omitted them, so tests that stub those builders
hit undefined on the mocked module.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(chat): re-attach scroll listener once container mounts
The scroll effect's empty dep array meant it ran only on the first
render, when `chatConfig` is still loading and the component returns
`<ChatLoadingState />` — so `messagesContainerRef.current` was null and
the listener was never attached. Depend on the gating conditions that
control which tree renders, so the effect re-runs once the real
container is in the DOM (and re-attaches when toggling in/out of voice
mode).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(chat): reset chat state on identifier change via key prop
Keying `<ChatClient>` on `identifier` guarantees a full remount on
route transitions between chats, so `conversationId`, `messages`, and
every other piece of local state start fresh — no reset effect
required.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(settings): restore paste-to-destructure for workspace secrets, cleanup hooks and design tokens
Restores the env-var paste feature (KEY=VALUE → split rows, multi-line
→ multi rows) for workspace secrets that was lost when the unified
Credentials tab was split into Secrets and Integrations. Adds
`parseEnvVarLine`, `parseValidEnvVars`, and `handleWorkspacePaste` with
full support for export prefix, quoted values, inline comments, and
base64 false-positive guards. Also adds consistent value masking
(show on focus / mask on blur) to new workspace input rows.
Cleans up ~20 unnecessary `useCallback` wrappers, fixes a direct state
mutation in `handleSingleValuePaste`, moves `e.preventDefault()` inside
the `parsedVars.length > 0` guard, replaces all hardcoded hex colors
with CSS variable tokens, converts template-literal classNames to `cn()`,
and replaces raw `<button>` with emcn `Button`.
* fix(settings): fix handlePaste silent swallow, quote-strip bug, and credential sync efficiency
- Move e.preventDefault() inside parsedVars guard in handlePaste so KEY= lines
don't silently discard input (mirrors handleWorkspacePaste fix from same PR)
- Add value.length >= 2 guard before quote-stripping to prevent single-char
values like KEY=\" from being stripped to empty and silently dropped
- Introduce createWorkspaceEnvCredentials and deleteWorkspaceEnvCredentials
for delta-aware credential sync (O(k) instead of O(n*m) for env var mutations)
- Fix createWorkspaceEnvCredentials early-return bug that skipped credential
record creation when workspace had zero members
- Update credentials/[id] DELETE to use deleteWorkspaceEnvCredentials instead
of full syncWorkspaceEnvCredentials
- Optimize syncWorkspaceEnvCredentials to fetch workspace+member IDs in parallel
once instead of once per credential
* fix(settings): normalize Windows line endings in paste handlers
* fix(settings): eliminate double-parse in handlePaste by inlining handleKeyValuePaste
* fix(billing): route scope by subscription referenceId, sync plan from Stripe, transfer storage on org join
Route every billing decision (usage limits, credits, storage, rate
limit, threshold billing, webhooks, UI permissions) through the
subscription's `referenceId` instead of plan-name heuristics. Fixes
the production state where a `pro_6000` subscription attached to an
organization was treated as personal Pro by display/edit code while
execution correctly enforced the org cap.
Scope
- Add `isOrgScopedSubscription(sub, userId)` (pure) and
`isSubscriptionOrgScoped(sub)` (async DB-backed) helpers. One is
used wherever a user perspective is available; the other in webhook
handlers that only have a subscription row.
- Replace plan-name scope checks in ~20 files: usage/limit readers,
credits balance + purchase, threshold billing, storage limits +
tracking, rate limiter, invoice + subscription webhooks, seat
management, membership join/leave, `switch-plan` admin gate,
admin credits/billing routes, copilot 402 handler, UI subscription
settings + permissions + sidebar indicator, React Query types.
Plan sync
- Add `syncSubscriptionPlan(subscriptionId, currentPlan, planFromStripe)`
called from `onSubscriptionComplete` and `onSubscriptionUpdate` so
the DB `plan` column heals on every Stripe event. Pro->Team upgrades
previously updated price, seats, and referenceId but left `plan`
stale — this is what produced the `pro_6000`-on-org row.
Priority + grace period
- `getHighestPrioritySubscription` now prefers org over personal
within each tier (Enterprise > Team > Pro, org > personal at each).
A user with a `cancelAtPeriodEnd` personal Pro who joins a paid org
routes pooled resources to the org through the grace window.
- `calculateSubscriptionOverage` personal-Pro branch reads user_stats
directly (bypassing priority) and bills only `proPeriodCostSnapshot`
when the user joined a paid org mid-cycle, so post-join org usage
isn't double-charged on the personal Pro's final invoice.
`resetUsageForSubscription` mirrors this: preserves
`currentPeriodCost` / `currentPeriodCopilotCost` when
`proPeriodCostSnapshot > 0` so the org's next cycle-close captures
post-join usage correctly.
Uniform base-price formula
- `basePrice × (seats ?? 1)` everywhere: `getOrgUsageLimit`,
`updateOrganizationUsageLimit`, `setUsageLimitForCredits`,
`calculateSubscriptionOverage`, threshold billing,
`syncSubscriptionUsageLimits`, `getOrganizationBillingData`.
Admin dashboard math now agrees with enforcement math.
Storage transfer on join
- Invitation-accept flow moves `user_stats.storageUsedBytes` into
`organization.storageUsedBytes` inside the same transaction when
the org is paid.
- `syncSubscriptionUsageLimits` runs a bulk-backfill version so
members who joined before this fix, or orgs that upgraded from
free to paid after members joined, get pulled into the org pool
on the next subscription event. Idempotent.
UX polish
- Copilot 402 handler differentiates personal-scoped ("increase your
usage limit") from org-scoped ("ask an owner or admin to raise the
limit") while keeping the `increase_limit` action code the parser
already understands.
- Duplicate-subscription error on team upgrade names the existing
plan via `getDisplayPlanName`.
- Invitation-accept invalidates subscription + organization React
Query caches before redirect so settings doesn't flash the user's
pre-join personal view.
Dead code removal
- Remove unused `calculateUserOverage`, and the following fields on
`SubscriptionBillingData` / `getSimplifiedBillingSummary` that no
consumer in the monorepo read: `basePrice`, `overageAmount`,
`totalProjected`, `tierCredits`, `basePriceCredits`,
`currentUsageCredits`, `overageAmountCredits`, `totalProjectedCredits`,
`usageLimitCredits`, `currentCredits`, `limitCredits`,
`lastPeriodCostCredits`, `lastPeriodCopilotCostCredits`,
`copilotCostCredits`, and the `organizationData` subobject. Add
`metadata: unknown` to match what the server returns.
Notes for the triggering customer
- The `pro_6000`-on-org row self-heals on the next Stripe event via
`syncSubscriptionPlan`. For the one known customer, a direct
UPDATE is sufficient:
`UPDATE subscription SET plan='team_6000' WHERE id='aq2...' AND plan='pro_6000'`.
Made-with: Cursor
* fix tests
* address more comments
* progress
* harden further
* outbox service
* address comments
* address comment on check
* simplify
* cleanup code
* minor improvement
* fix(blocks): resolve variable display in mothership resource preview
Variables block showed empty assignments in the embedded workflow preview
because currentWorkflowId was read from URL params, which don't contain
workflowId in the mothership route. Fall back to activeWorkflowId from
the workflow registry.
* fix(blocks): narrow currentWorkflowId to string to satisfy strict null checks
* feat(tables): add column selection, missing keyboard shortcuts, and Sheets-aligned operations
Click column headers to select entire columns, shift-click to extend to
a column range. Delete, cut, and copy operations work on column
selections with full undo/redo support. Adds Home, End, Ctrl+Home,
Ctrl+End, PageUp, PageDown, Ctrl+Space, and all Shift variants.
Changes Ctrl+A to select all cells instead of checkbox rows. Column
header dropdown menu now opens on right-click instead of left-click.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): chevron opens dropdown, drag header to reorder columns
Split column header into label area (click to select, draggable for
reorder) and chevron button (click to open dropdown menu). Remove
the grip handle — dragging the header itself now reorders columns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): full-column highlight during drag reorder
Replace the thin 2px line drop indicator with a full-column highlight
that spans the entire table height, matching Google Sheets behavior.
The insertion line is still shown at the drop edge for precision.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): handle drag reorder edge cases, dim source column
Suppress drop indicator when drag would result in no position change
(dragging onto self or adjacent no-op positions). Dim the source
column body cells during drag with a background overlay. Skip the
API call when the computed order is identical to the current order.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(tables): add column reorder undo/redo, body drop targets, and escape cancel
Column drag-and-drop now supports dropping anywhere in a column (not just headers),
pressing Escape to cancel a drag, and full undo/redo integration for column reordering.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): merge partial updates in updateRow to prevent column data loss
When Mothership called updateRow directly (bypassing the PATCH API route),
it passed only the changed fields — which were written as the entire row,
wiping all other columns. Move the merge logic into updateRow itself so
all callers get correct partial-update semantics, and remove the now-redundant
pre-merge from both PATCH routes.
* test(tables): add updateRow partial merge tests
Covers the bug where partial updates wiped unmentioned columns — verifies
that fields not in the update payload are preserved, nulling a field works,
full-row updates are idempotent, and missing rows throw correctly.
* feat(tables): add delete-column undo/redo, rename metadata sync, and comprehensive row ID patching
- Delete column now captures column definition, cell data, order, and width for full undo/redo
- Column rename undo/redo now properly syncs columnWidths and columnOrder metadata
- patchRedoRowId/patchUndoRowId extended to handle all action types containing row IDs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): remove source column dimming during drag reorder
Only show the insertion line at the drop position, matching Google Sheets
behavior. Remove dragSourceBounds memo and isDragging prop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): preserve selection on right-click, auto-resize on double-click, fix escape during drag
- Right-clicking within an existing selection now preserves it instead of
resetting to a single cell, so context menu operations apply to the full range
- Double-clicking a column border auto-resizes the column to fit its content
- Escape during column drag now immediately clears refs before state update,
preventing the dragend handler from executing the reorder
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): add aria-hidden value and aria-label for column header accessibility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): tighten auto-resize padding to match Google Sheets
Reduce header padding from +48px to +36px (icon + cell padding) and cell
padding from +20px to +17px (cell padding + border) for a snug fit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): clean drag ghost and clear selection on drag start
- Create a minimal custom drag image showing only the column name instead
of the browser's default ghost that includes adjacent columns/checkboxes
- Clear any existing cell/column selection when starting a column drag to
prevent stale highlights from persisting during reorder
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(tables): add Shift+Space row selection and Ctrl+D fill down
Shift+Space now selects the entire row (all columns) instead of toggling
a checkbox, matching Google Sheets behavior. Ctrl+D copies the top cell's
value down through the selected range with full undo/redo support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): show toast on incompatible column type change
The server validates type compatibility and returns a clear error message
(e.g. "3 row(s) have incompatible values"), but the client was silently
swallowing it. Now surfaces the error via toast notification. Also moved
the undo push to onSuccess so a failed type change doesn't pollute the
undo stack.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): scroll-into-view for selection focus, Home/End origin, delete-column undo timing
- Scroll-into-view now tracks selectionFocus (not just anchor), so
Shift+Arrow extending selection off-screen properly auto-scrolls
- Shift+Home/End now uses the current focus as origin (matching
Shift+Arrow behavior) instead of always using anchor
- Delete column undo entry is now pushed in onSuccess, preventing
a corrupted undo stack if the server rejects the deletion
- Dialog copy updated from "cannot be undone" to "You can undo this
action" since undo/redo is supported
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve duplicate declarations from rebase against staging
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix file upload
* fix(tables): merge column widths on delete-column undo, try/finally for auto-resize
- Delete-column undo now reads current column widths via getColumnWidths
callback and merges the restored column's width into the full map,
preventing other columns' widths from being wiped
- Auto-resize measurement span is now wrapped in try/finally to ensure
DOM cleanup if an exception occurs during measurement
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: revert accidental home.tsx change from rebase conflict resolution
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): clear isColumnSelection on double-click and right-click, skip scroll for column select
- Clear isColumnSelection when double-clicking a cell to edit, preventing
the column selection effect from fighting with the editing state
- Clear isColumnSelection when right-clicking outside the current
selection, preventing stale column selection from re-expanding
- Skip scroll-into-view when isColumnSelection is true, preventing
the viewport from jumping to the bottom row when clicking a column header
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): remove inline font override in auto-resize, guard undefined columnOrder
- Remove `font:inherit` from measurement span inline style so Tailwind
classes (font-medium, text-small) control font properties for accurate
column width measurement
- Only include columnOrder in metadata update when defined, preventing
handleColumnRename from clearing a persisted column order when
columnOrderRef is null
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): capture columnRequired in delete-column undo for full restoration
The delete-column undo action captured columnUnique but not columnRequired,
so undoing a delete on a required column would silently drop the constraint.
Now captures and restores both constraints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): restore width independently of order on delete-column undo, batch fill-down
- Column width restoration in delete-column undo no longer requires
previousOrder to be non-null — width is restored independently
- Ctrl+D fill-down now uses batchUpdateRef (single API call) instead
of calling mutateRef per row in a loop
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): multi-column delete, select-all cell model, cut flash, chevron alignment
- Multi-select delete: detect column selection range and delete all selected
columns sequentially with individual undo entries
- Select all (header checkbox): use cell selection model instead of checkbox
model for consistent highlighting
- Cut flash: batch cell clears into single mutation to prevent stale data
flashing from multiple onSettled invalidations
- Chevron alignment: adjust right padding from pr-2 to pr-2.5
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): restore column width locally on delete-column undo
Add onColumnWidthsChange callback to undo hook so restored column
widths update local component state, not just server metadata.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): prevent Ctrl+D bookmark dialog, batch Delete/Backspace mutations
- Move e.preventDefault() before early returns in Ctrl+D handler so
the browser bookmark dialog is always suppressed
- Replace per-row mutateRef calls with single batchUpdateRef call in
both Delete/Backspace handlers (checked rows and cell selection),
consistent with cut and fill-down
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): adjust column positions for multi-column delete undo
Capture original schema positions upfront and adjust each by the
count of previously-deleted columns with lower positions, so undo
restores columns at correct server-side positions in LIFO order.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): only multi-delete when clicked column is within selection
Check that the right-clicked column is within the selected column
range before using multi-column delete. If the click is outside the
selection, delete only the clicked column.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): prevent duplicate undo entry on column drag-drop
Clear dragColumnNameRef immediately in handleColumnDragEnd so the
second invocation (from dragend after drop already fired) is a no-op.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): clean up width on delete-column redo, suppress click during drag
- Redo path for delete-column now removes the column's width from
metadata and local state, preventing stale width entries
- Add didDragRef to ColumnHeaderMenu to suppress the click event
that fires after a drag operation, preventing selection flash
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): remove unstable mutation object from useCallback deps
deleteTableMutation is not referentially stable — only .mutateAsync()
is. Including the mutation object causes unnecessary callback recreation
on every mutation state change.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): fix auto-resize header padding, deduplicate rename metadata logic
Increase header text measurement padding from 36px to 57px to account
for the chevron dropdown button (pl-0.5 + 9px icon + pr-2.5) that
always occupies layout space. Prevents header text truncation on
auto-resize.
Deduplicate column rename metadata logic by having columnRename.onSave
call handleColumnRename instead of reimplementing the same width/order
transfer and metadata persist.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): log error on cell data restoration failure during undo
Add onError handler to the batchUpdateRowsMutation inside
delete-column undo so failures are logged instead of silently
swallowed. The column schema restores first, and the cell data
restoration is a separate async call that the outer try/catch
cannot intercept.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): address audit findings across table, undo hook, and store
- Add missing bounds check in handleCopy (c >= cols.length) matching
handleCut for defensive consistency
- Clear lastCheckboxRowRef in Ctrl+Space and Shift+Space to prevent
stale shift-click checkbox range after keyboard selection
- Fix stale snapshot race in patchRedoRowId/patchUndoRowId by reading
state inside the set() updater instead of via get() outside it
- Add metadata cleanup to create-column undo so column width is removed
from both local state and server, symmetric with delete-column redo
- Remove stale width key from columnWidths on column delete instead of
persisting orphaned entries
- Normalize undefined vs null in handleInlineSave change detection to
prevent unnecessary mutations when oldValue is undefined
- Use ghost.parentNode?.removeChild instead of document.body.removeChild
in drag ghost cleanup to prevent throw on component unmount
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tables): reset didDragRef in handleDragEnd to prevent stale flag
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(pdf): PDF previews by adding the missing preview endpoint and allowing same-origin blob URLs in iframe CSP
* fixed
* add preview routes and tests
* follow nextjs route gen strat
* fix(fireflies): support V2 webhook payload format for meetingId mapping
Fireflies V2 webhooks use snake_case field names (meeting_id, event,
client_reference_id) instead of camelCase (meetingId, eventType,
clientReferenceId). The formatInput handler now auto-detects V1 vs V2
payloads and maps fields correctly, fixing empty meetingId on V2 webhooks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(fireflies): guard against NaN timestamp, use stricter V2 detection
Address PR review feedback:
- Use Number.isFinite guard to prevent NaN timestamp propagation
- Use AND instead of OR for V2 detection since both meeting_id and
event are required fields in every V2 payload
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(execution): run pptx/docx/pdf generation inside isolated-vm sandbox
Retires the legacy doc-worker.cjs / pptx-worker.cjs pipeline that ran user
DSL via node:vm + full require() in the same UID/PID namespace as the main
Next.js process. User code now runs inside the existing isolated-vm pool
(V8 isolate, no process / require / fs, no /proc/1/environ reachability).
Introduces a first-class SandboxTask abstraction under apps/sim/sandbox-tasks/
that mirrors apps/sim/background/ — one file per task, central typed
registry, kebab-case ids. Adding a new thing that runs in the isolate is
one file plus one registry entry.
Runtime additions in lib/execution/:
- task-mode execution in isolated-vm-worker.cjs: load pre-built library
bundles, run task bootstrap, run user code, run finalize, transfer
Uint8Array result as base64 via IPC
- named broker IPC bridge (generalizes the existing fetch bridge) with
args size, result size, and per-execution call caps
- cooperative AbortSignal support: cancel IPC disposes the isolate, pool
slot is freed, pending broker-call timers are swept
- compiled scripts + references explicitly released per execution
- isolate.isDisposed used for cancellation detection (no error-string
substring matching)
Library bundles (pptxgenjs, docx, pdf-lib) are built into isolate-safe
IIFE bundles by apps/sim/lib/execution/sandbox/bundles/build.ts and
committed; next.config.ts / trigger.config.ts / Dockerfile updated to
ship them instead of the deleted dist/*-worker.cjs artifacts.
Call sites migrated:
- app/api/workspaces/[id]/pptx/preview/route.ts
- app/api/files/serve/[...path]/route.ts (+ test mock)
- lib/copilot/tools/server/files/{workspace-file,edit-content}.ts
All pass owner key user:<userId> for per-user pool fairness + distributed
lease accounting.
Made-with: Cursor
* improvement(sandbox): delegate timers to Node, add phase timings + saturation logs
Follow-ups on top of the isolated-vm migration (da14027b2):
Timer delegation (laverdet/isolated-vm#136 recommended pattern):
- setTimeout / setInterval / clearTimeout / clearImmediate delegate to
Node's real timer heap via ivm.Reference. Real delays are honored;
clearTimeout actually cancels; ms is clamped to the script timeout
so callbacks can't fire after the isolate is disposed.
- Per-execution timer tracking + dispose-sweep in finally. Zero stale
callbacks post-dispose.
- unwrapPrimitive helper normalizes ivm.Reference-wrapped primitives
(arguments: { reference: true } applies uniformly to all args).
- _polyfills.ts shrinks from ~130 lines to the global->globalThis alias.
Timers / TextEncoder / TextDecoder / console all install per-execution
from the worker via ivm bridges.
AbortSignal race fix (pre-existing bug surfaced by the timer smoke):
- Listener is registered after await tryAcquireDistributedLease. If the
signal aborted during that ~200ms window (Redis down), AbortSignal
doesn't fire listeners registered after the fact — the abort was
silently missed. Now re-checks signal.aborted synchronously after
addEventListener.
Observability:
- executeTask returns IsolatedVMTaskTimings (setup, runtimeBootstrap,
bundles, brokerInstall, taskBootstrap, harden, userCode, finalize,
total) in every success + error path. run-task.ts logs these with
workspaceId + queueMs so 'which tenant is slow' is queryable.
- Pool saturation events now emit structured logger.warn with reason
codes: queue_full_global, queue_full_owner, queue_wait_timeout,
distributed_lease_limit. Matches the existing broker reject pattern.
Security policy:
- New .cursor/rules/sim-sandbox.mdc codifies the hard rules for the
worker process: no app credentials, all credentialed work goes
through host-side brokers, every broker scopes by workspaceId.
Pre-merge checklist for future changes to isolated-vm-worker.cjs.
Measured phase breakdown (local smoke, Redis down): pptx wall=~310ms
with bundles=~16ms, finalize=~83ms; docx ~290ms / 17ms / 70ms; pdf
~235ms / 17ms / 5ms. Bundle compilation is not the bottleneck —
library finalize is.
Made-with: Cursor
* fix(sandbox): thread AbortSignal into runSandboxTask at every call site
Three remaining callers of runSandboxTask were not threading a
cancellation signal, so a client disconnect mid-compile left the pool
slot occupied for the full 60s task timeout. Matching the pattern the
pptx-preview route already uses.
- apps/sim/app/api/files/serve/[...path]/route.ts — GET forwards
`request.signal` into handleLocalFile / handleCloudProxy, which
forward into compileDocumentIfNeeded, which forwards into
runSandboxTask.
- apps/sim/lib/copilot/tools/server/files/workspace-file.ts — passes
`context.abortSignal` (transport/user stop) into runSandboxTask.
- apps/sim/lib/copilot/tools/server/files/edit-content.ts — same.
Smoke: simulated client disconnect at t=1000ms during a task that would
otherwise have waited 10s. The pool slot unwinds at t=1002ms with
AbortError; previously would have sat 60s until the task-level timeout.
Made-with: Cursor
* chore(build): raise node heap to 8GB for next build type-check
Next.js's type-check worker OOMs at the default 4GB heap on Node 23 for
this project's type graph size. Bumps the heap to 8GB only for the
`next build` invocation inside `bun run build`.
Docker builds are unaffected — `next.config.ts` sets
`typescript.ignoreBuildErrors: true` when DOCKER_BUILD=1, which skips
the type-check pass entirely. This only fixes local `bun run build`.
No functional code changes.
Made-with: Cursor
* fix lint
* refactor(copilot): dedup getDocumentFormatInfo across copilot file tools
The same extension -> { formatName, sourceMime, taskId } mapping was
duplicated in workspace-file.ts and edit-content.ts. Any future format
or task-id change had to happen in two places.
Exports getDocumentFormatInfo + DocumentFormatInfo from workspace-file.ts
(which already owned the PPTX/DOCX/PDF source MIME constants) and
imports it in edit-content.ts. Same source-of-truth pattern the file
already uses for inferContentType.
Made-with: Cursor
* fix(sandbox): propagate empty-message broker/fetch errors
Both bridges in the isolate used truthiness to detect host-side errors:
if (response.error) throw new Error(response.error); // broker
if (result.error) throw new Error(result.error); // fetch
If a host handler ever threw `new Error('')`, err.message would be ''
(falsy), so { error: '' } was silently swallowed and the isolate saw
a successful null result. Existing call sites don't throw empty-message
errors, but the pattern was structurally unsafe.
Switch both to typeof check === 'string' and fall back to a default
message if the string is empty, so all host-reported errors propagate
into the isolate regardless of message content.
Made-with: Cursor
* improvement(mothership): agent model dropdown validations, recommendation system
* mark a few more models:
* remove regex based checks'
* remove dead code
* remove inherited reseller flags
* fix note
* address bugbot comments
* code cleanup
Replaces MutationObserver on document.documentElement (watching CSS variable
changes) + window resize listener with a ResizeObserver on the terminal element
itself. The terminal now measures its own rendered width directly, so it responds
correctly to all layout changes — sidebar, workflow panel, and mothership resize —
without indirect CSS variable plumbing or cross-component coupling.
* fix(docs): preserve gif playback position in lightbox and clean up ui components
- Capture currentTime on click and seek lightbox video to match using useLayoutEffect
- Convert lightboxStartTime from useState to useRef (no independent render needed)
- Apply same fix to ActionVideo in action-media.tsx
- Remove dead AnimatedBlocks component (zero imports)
- Fix language-dropdown to derive currentLang during render instead of mirroring into state via effect
- Replace template literals with cn() in faq.tsx and video.tsx
* fix(chat): prevent @-mention menu focus loss and stabilize render identity
Radix DropdownMenu's FocusScope was restoring focus from the search input
to the content root whenever registered menu items mounted or unmounted
inside the content, interrupting typing after a keystroke or two.
- Keep the default tree always mounted under `hidden` instead of swapping
subtrees when the filter activates.
- Render filtered results as plain <button role="menuitem"> so they do not
participate in Radix's menu Collection.
- Add activeIndex state with ArrowUp/Down/Enter keyboard nav, mouse-hover
sync, and scrollIntoView so the highlighted row stays visible and users
can see what Enter will select.
While tracing the cascade that compounded the bug:
- Hoist `select` in useWorkflowMap / useWorkspacesQuery / useFolderMap to
module scope so TanStack Query caches the select result across renders.
- Guard setSelectedContexts([]) with a functional updater that bails out
when already empty, preventing a fresh [] literal from invalidating
consumers that key on reference identity.
- Wrap WorkspaceHeader in React.memo so it bails out on parent renders
once its (now-stable) props are unchanged.
Made-with: Cursor
* remove extraneous comments
* cleanup
* fix(chat): apply same setState bail-out to clearContexts for consistency
Matches the invariant we already established for the message effect:
calling setSelectedContexts([]) against an already-empty array emits a
fresh [] reference (Object.is bails out are not reference-level), which
cascades through consumers that key on selectedContexts identity.
clearContexts is part of the hook's public API so callers can't know
whether the list is empty — make it safe for them.
Made-with: Cursor
* improvement(utils): add shared utility functions and replace inline patterns
Add sleep, toError, safeJsonParse, isNonNull helpers and invariant/assertNever
assertions. Replace all inline implementations across the codebase with these
shared utilities for consistency. Zero behavioral changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(agiloft): remove import type from .server module to fix client bundle build
Turbopack resolves .server.ts modules even for type-only imports,
pulling dns/promises into client bundles. Define SecureFetchResponse
locally instead.
* fix(agiloft): revert to client-safe imports to fix build
The SSRF upgrade to input-validation.server introduced dns/promises
into client bundles via tools/registry.ts. Revert to the original
client-safe validateExternalUrl + fetch. The SSRF DNS-pinning upgrade
for agiloft directExecution should be done via API routes in a
separate PR.
* feat(agiloft): add API route for retrieve_attachment, matching established file patterns
Convert retrieve_attachment from directExecution to standard API route
pattern, consistent with Slack download and Google Drive download tools.
- Create /api/tools/agiloft/retrieve with DNS validation, auth lifecycle,
and base64 file response matching the { file: { name, mimeType, data,
size } } convention
- Update retrieve_attachment tool to use request/transformResponse
instead of directExecution, removing the dependency on
executeAgiloftRequest from the tool definition
- File output type: 'file' enables FileToolProcessor to store downloaded
files in execution filesystem automatically
* shopify
* fix(agiloft): add optional flag to nullable lock record block outputs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(agiloft): revert optional flag on block outputs — property only exists on tool outputs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore(utils): remove unused utilities (asserts, safeJsonParse, isNonNull)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* improvement(sidebar): interleave folders and workflows by sort order in all resource pickers
- Merge folder/workflow submenus into a single Workflows tree sorted by sortOrder in both the @ plus-menu and add-resource dropdowns
- Widen both dropdowns from 240px to 320px and remove type labels from search results
- Fix isOpen/onSwitch regression: WorkflowFolderTreeItems now forwards node.isOpen so already-open tabs are switched to rather than duplicated
- Apply same interleaved sortOrder ordering to the collapsed sidebar's root-level folder+workflow list
* fix(add-resource-dropdown): align sort tiebreaker with compareByOrder, document empty-folder omission
Use id.localeCompare as the sort tiebreaker in buildWorkflowFolderTree to match the sidebar's
compareByOrder fallback (sortOrder → id) instead of name. Add a comment clarifying that empty
folders are intentionally omitted from the tree view.
* chore: remove extraneous inline comment
* feat(monday): add full Monday.com integration with tools, block, triggers, and OAuth
Adds a comprehensive Monday.com integration:
- 13 tools: list/get boards, CRUD items, search, subitems, updates, groups, move, archive
- Block with operation dropdown, board/group selectors, OAuth credential, advanced mode
- 9 webhook triggers with auto-subscription lifecycle (create/delete via GraphQL API)
- OAuth config with 7 scopes (boards, updates, webhooks, me:read)
- Provider handler with challenge verification, formatInput, idempotency
- Docs, icon, selectors, and all registry wiring
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(monday): cast userId to string in deleteSubscription fallback
The DeleteSubscriptionContext type has userId as unknown, causing a
TypeScript error when passing it to getOAuthToken which expects string.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(monday): escape string params in GraphQL, align deleteSubscription with established patterns
- Use JSON.stringify() for groupId in get_items.ts (matches create_item.ts
and move_item_to_group.ts)
- Use JSON.stringify() for notificationUrl in webhook provider
- Remove non-standard getOAuthToken fallback in deleteSubscription to match
Airtable/Webflow pattern (credential resolution only, warn and return on failure)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(monday): sanitize columns JSON in search_items GraphQL query
Parse and re-stringify the columns param to ensure well-formed JSON
before interpolating into the GraphQL query, preventing injection
via malformed input.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(monday): validate all numeric IDs and sanitize columns in GraphQL queries
- Add sanitizeNumericId() helper to tools/monday/utils.ts for consistent
validation across all tool body builders
- Apply to all 13 instances of boardId, itemId, parentItemId interpolation
across 11 tool files, preventing GraphQL injection via crafted IDs
- Wrap JSON.parse in search_items.ts with try-catch for user-friendly
error on malformed column filter JSON
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(monday): deduplicate numeric ID validation, sanitize limit/page params
- Refactor sanitizeNumericId to delegate to validateMondayNumericId
from input-validation.ts, eliminating duplicated regex logic
- Add sanitizeLimit helper for safe integer coercion with bounds
- Apply sanitizeLimit to limit/page params in list_boards, get_items,
and search_items for consistent validation across all GraphQL params
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(monday): align list_boards limit description with code (max 500)
The param description said "max 100" but sanitizeLimit caps at 500,
which is what Monday.com's API supports for boards. Updated both the
tool description and docs to say "max 500".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(triggers): add Atlassian triggers for Jira, JSM, and Confluence
- Jira: add 9 new triggers (sprint created/started/closed, project created, version released, comment updated/deleted, worklog updated/deleted)
- JSM: add 5 triggers from scratch (request created/updated/commented/resolved, generic webhook)
- Confluence: add 7 new triggers (comment updated, attachment updated, page/blog restored, space removed, page permissions updated, user created)
- Add JSM webhook provider handler with HMAC validation and changelog-based event matching
- Add Atlassian webhook identifier to idempotency service for native dedup
- Add extractIdempotencyId to Confluence handler
- Fix Jira generic webhook to pass through full payload for non-issue events
- Fix output schemas: add description (ADF), updateAuthor, resolution, components, fixVersions, worklog timestamps, note emailAddress as Jira Server only
* fix(triggers): replace any with Record<string, unknown> in confluence extract functions
* lint
* fix(triggers): use comment.id in JSM idempotency, fix confluence type cast
JSM extractIdempotencyId now prioritizes comment.id over issue.id for
comment_created events, matching Jira's documented webhook payload
structure. Also fixes type cast for confluence extract function calls.
* fix(triggers): correct comment.body type to json, fix TriggerOutput description type
- JSM webhook comment.body changed from string to json (ADF format)
- Widened TriggerOutput.description to accept TriggerOutput objects,
removing unsafe `as unknown as string` casts for Jira description fields
Previously, Non-ASCII characters (like Korean) in workflow names were
replaced by dashes during export because of a restrictive regex.
This update uses a Unicode-aware regex to allow letters and numbers
from any language while still sanitizing unsafe filesystem characters.
fixes#4119
Signed-off-by: JaeHyung Jang <jaehyung.jang@navercorp.com>
* improvement(logs): fix trigger badge wrapping, time range picker, status filters, and React anti-patterns
* chore(logs): remove dev mock logs
* fix(logs): prevent DatePicker onOpenChange from reverting time range after Apply
* fix(socket): sync deploy button state across collaborators
Broadcast workflow-deployed events via socket so all connected users
invalidate their deployment query cache when any user deploys, undeploys,
activates a version, or triggers a deploy through chat/form endpoints.
* fix(socket): check response status on deployment notification
Log a warning when the socket server returns a non-2xx status for
deployment notifications, matching the pattern in lifecycle.ts.
* improvement(config): consolidate socket server URL into getSocketServerUrl/getSocketUrl
Replace all inline `env.SOCKET_SERVER_URL || 'http://localhost:3002'` and
`getEnv('NEXT_PUBLIC_SOCKET_URL') || 'http://localhost:3002'` with centralized
utility functions in urls.ts, matching the getBaseUrl() pattern.
* improvement(config): consolidate Ollama URL and CSP socket/Ollama hardcodes
Add getOllamaUrl() to urls.ts and replace inline env.OLLAMA_URL fallbacks
in the provider and API route. Update CSP to use getSocketUrl(),
getOllamaUrl(), and a local toWebSocketUrl() helper instead of hardcoded
localhost strings.
* lint
* fix(tests): add missing mocks for new URL utility exports
Update lifecycle, async execute, and chat manage test mocks to include
getSocketServerUrl, getOllamaUrl, and notifySocketDeploymentChanged.
* fix(csp): remove urls.ts import to fix next.config.ts build
CSP is loaded by next.config.ts which transpiles outside the @/ alias
context. Use local constants instead of importing from urls.ts.
* fix(queries): invalidate chat and form status on deployment change
Add chatStatus and formStatus to invalidateDeploymentQueries so all
deployment-related queries refresh when any user deploys or undeploys.
* improvement(ui): remove React anti-patterns, fix CSP violations
* fix(ui): restore useMemo on existingKeys — it is observed by useAvailableResources
* improvement(ui): add RefreshCw icon, update Bell SVG, active state styling for header actions
* minor UI improvements
* feat(docs): fill documentation gaps across platform features
* fix(docs): address PR review comments on chat OTP cookies and MCP env var placeholders
* fix(docs): replace smart quotes with straight quotes in JSX attributes
* update(docs): update mcp, custom tools, and variables docs
* Fix grammar
* mothership docs, tags, connectors, api, chat deploy, etc
* more info
* more
* feat(docs): auto-generate per-provider trigger documentation
Extends scripts/generate-docs.ts to produce one MDX page per trigger
provider (39 pages) in apps/docs/content/docs/en/triggers/. The 5
hand-written pages (index, start, schedule, webhook, rss) are never
touched.
Key additions to the generation script:
- resolveConstVariable() resolves module-level const spreads so
providers like Vercel that build outputs from const variables (not
just functions) are fully documented
- resolveTriggerBuilderFunction() extended to expand variable spreads
(...varName) in addition to function-call spreads (...fn())
- groupTriggersByProvider() deduplicates v1/v2 trigger variants by
name, keeping the highest-versioned one per provider
- writeIconMapping() adds bare-name aliases for versioned block types
(github_v2 → github, fireflies_v2 → fireflies, etc.) so
BlockInfoCard resolves icons for all 39 trigger providers
- extractTriggerConfigFields() filters readOnly display blocks (webhook
URL displays, sample payloads, curl examples) from config tables
Each generated page includes: BlockInfoCard with correct icon/color,
trigger count, polling note where applicable, Configuration table, and
Output table for every trigger. No "Type:" lines.
* refactor(docs): align trigger docs structure with tools docs
- Use ### `trigger_id` headings (matching ### `tool_id` in tools docs)
- Wrap all trigger sections under a ## Triggers header
- Rename Configuration/Output to #### level (matching #### Input/Output)
- Use Parameter column header to match tools docs table style
- Map UI widget types to semantic types: short-input/long-input/dropdown
→ string, switch → boolean, slider → number, oauth-input → string
* refactor(docs): use human-readable names for trigger section headings
Trigger IDs are internal identifiers; users scan by name. Switch from
### `trigger_id` to ### Trigger Name for cleaner sidebar navigation
and better readability.
* fix(docs): resolve subBlock builder functions for all trigger Config sections
Extends generate-docs.ts to parse subBlock builder functions so all 15
providers previously missing Configuration sections now generate them.
Handles three patterns:
- `buildTriggerSubBlocks({extraFields: buildX(...)})` — extracts extra
fields from the call site and resolves them from the provider's utils.ts
- `return [...]` — direct array return (Attio, Confluence, etc.)
- `blocks.push(...)` — imperative push pattern (Linear, Ashby)
Also resolves const-reference field IDs (SCREAMING_CASE) by searching
the webhook provider constants cache, fixing Gong's `gongJwtPublicKeyPem`
field which was previously unresolvable. Adds title-as-description fallback
for OAuth credential fields that have no explicit description.
* fix(docs): correctly destructure nested implicit-object trigger outputs
Fixes a parser bug where output fields with no top-level `type` key but
child fields each having their own `type`/`description` were incorrectly
parsed. The `type:` and `description:` regex matches were not
depth-aware, so values from nested children bled into the parent field.
Changes:
- Add `isAtDepthZero()` helper for brace-depth-aware regex matching
- Fix `parseFieldContent` to only match `type:` at brace depth 0
- Fix `extractDescription` to only match `description:` at brace depth 0
- Add implicit-object fallback: when no top-level `type` exists but child
fields have their own types, treat as `object` with `properties`
- Regenerate all affected trigger docs (Cal.com payload, Linear data,
Jira issue.fields, Ashby application, Greenhouse candidate, etc.)
* chore(docs): update static trigger and start page images
* feat(providers): add claude-opus-4-7 model with adaptive thinking support
* Add workflow version screenshots
* Add function block screenshots
---------
Co-authored-by: Theodore Li <theo@sim.ai>