The visual filter/sort builders read the selected tableId from subBlock id
'tableId', but the Table block stores it under 'tableSelector' (basic) or
'manualTableId' (advanced) via canonicalParamId. The lookup always returned
null, so useTable was disabled and the column picker always showed
"no options available".
Adds useCanonicalSubBlockValue that resolves by canonicalParamId through
the canonical index, mirroring the pattern used by dropdown dependsOn.
* improvement(tables): race-free row-count trigger + scoped tx timeouts
* fix(tables): close upsert race + serialize replaceTableRows
Two concurrency bugs flagged by review:
1. `upsertRow` insert path: removing FOR UPDATE broke serialization between
the initial existing-row SELECT and the INSERT. Two concurrent upserts
on the same conflict target could both find no match, then both insert,
producing a duplicate that bypasses the app-level unique check. Fixed
by re-checking for the matching row *after* acquiring the per-table
advisory lock; if a racing tx already inserted, switch to UPDATE.
2. `replaceTableRows`: under READ COMMITTED each tx's DELETE uses its own
MVCC snapshot, so two concurrent replaces could each DELETE only the
rows visible at their start, then both INSERT, leaving the table with
the union of both row sets. Fixed by acquiring the per-table advisory
lock at the start of the tx to serialize replaces against each other
and against auto-position inserts.
Also updated a stale docstring on `upsertRow` that still referenced the
removed FOR UPDATE.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(tables): serialize explicit-position inserts with advisory lock
The `(table_id, position)` index is non-unique. Concurrent explicit-
position inserts at the same slot can both observe the slot empty, both
skip the shift, then each INSERT — producing duplicate `(table_id,
position)` rows.
Take the per-table advisory lock in the explicit-position branches of
`insertRow` and `batchInsertRows` (the auto-position branches already do
this). Updated the test that asserted the explicit path skipped the lock,
and added coverage for `batchInsertRows` with explicit positions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(tables): dedupe upsert UPDATE path + extract nextAutoPosition
Two pure cleanups on the round-1 changes:
1. Extract `nextAutoPosition(trx, tableId)` — the `SELECT coalesce(max(
position), -1) + 1` pattern was repeated in three call sites
(`insertRow` auto branch, `batchInsertRows` auto branch, `upsertRow`
insert branch). One helper, same behavior.
2. Consolidate `upsertRow` update path. The initial-SELECT match and the
post-lock re-select match previously had two literal duplicates of the
same UPDATE + return block. Resolve `matchedRowId` first, then run one
UPDATE branch. Lock is still only acquired when we don't find a match
on the first pass.
No behavior change. 98/98 table unit tests unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(ashby): align tools, block, and triggers with Ashby API
Audit-driven refactor to destructure rich fields per Ashby's API docs,
centralize output shapes via shared mappers in tools/ashby/utils.ts,
and align webhook provider handler with trigger IDs via a shared
action map. Removes stale block outputs left over from prior flat
response shapes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ashby): remove stale noteId output and reject ping events
- Remove stale `noteId` output descriptor from block (create_note
now returns `id` at the top level via the shared note mapper).
- Explicitly reject `ping` events in the webhook matchEvent before
falling back to the generic triggerId check, so webhook records
missing triggerId cannot execute workflows on Ashby ping probes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ashby): trim optional ID params in create/update tools
Optional ID params in create_application, change_application_stage,
and update_candidate were passed through to the request body without
.trim(), unlike their required ID siblings. Normalize to prevent
copy-paste whitespace errors.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ashby): add subblock migration for removed filterCandidateId
Add SUBBLOCK_ID_MIGRATIONS entry so deployed workflows that previously
used the `filterCandidateId` subBlock on `list_applications` don't break
after the field was removed (Ashby's application.list doesn't filter by
candidateId). Also regenerate docs to sync noteId removal.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ashby): final API alignment from parallel validation
- create_candidate: email is optional per Ashby docs (only name is
required); tool, types, and block all made non-required.
- list_applications: guard NaN when createdAfter can't be parsed so we
don't send a bad value to Ashby's API.
- webhook provider: replace createHmacVerifier with explicit
fail-closed verifyAuth that 401s when secretToken, signature header,
or signature match is missing (was previously fail-open on missing
secret).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ashby): preserve input.data path in webhook formatInput
Restore the explicit `data` key alongside the spread so deployed
workflows that reference `input.data.application.*`, `input.data.candidate.*`,
etc. keep working. The spread alone dropped those paths.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(ashby): drop legacy input.data key from webhook formatInput
Keep formatInput aligned with the advertised trigger outputs schema
(flat top-level entities) and drop the legacy input.data.* compat path.
Every field declared in each trigger's outputs is now populated 1:1 by
the data spread plus the explicit action key — no undeclared keys.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ashby): trim remaining ID body params for parity
Add .trim() on sourceId (create_candidate), jobId (list_applications),
applicationId and interviewStageId (list_interviews) to match the
trim-on-IDs pattern used across the rest of the Ashby tools and guard
against copy-paste whitespace.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* update docs
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
AGENTS.md / CLAUDE.md forbid crypto.randomUUID() (non-secure contexts throw
TypeError in browsers). Four copilot server-side files still violated this
rule, left over after PR #3397 polyfilled the client.
Routes through request lifecycle, OAuth draft insertion, persisted message
normalization, and table-row generation now use generateId from @sim/utils/id,
which is a drop-in UUID v4 producer that falls back to crypto.getRandomValues
when randomUUID is unavailable.
Refs #3393.
* feat(agentphone): add AgentPhone integration
* fix(agentphone): validate numeric inputs and metadata JSON
* chore(agentphone): remove dead from fallback in get_number_messages
* fix(agentphone): drop empty-string updates in update_contact
* fix(agentphone): scope limit/offset to list ops and revert stray IdentityCenter change
* lint
* feat(files): default sort by updated and add updated sort option
* feat(files): show Last Updated column
Matches the visible-column pattern already used on Knowledge and Tables
so users can see the value they're sorting by.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(retention): switch data retention to be org-level
* fix lint
* cleanup mothership ran logs
* fix cleanup dispatcher
* fix ui flash for data retention settings
* fix lint
* remove raw sql string interprolation
* fix(api): return archivedAt for list tables route
* improvement(repo): restructuring to make realtime image narrower scoped
* improvements
* chore(repo): rebase fixes and quality improvements for realtime split
Addresses merge-time issues and gaps from the realtime app split:
- Retarget stale vi.mock paths to @sim/workflow-persistence/subblocks
- Restore README branding, fix AGENTS.md script reference
- Restore TSDoc on workflow-persistence subblocks helpers
- Use toError() from @sim/utils/errors in save.ts
- Add vitest config + local mocks so @sim/audit tests run standalone
- Move socket.io-client to devDependencies in apps/realtime
- Add missing package COPY steps to docker/app.Dockerfile
- Add check:boundaries/check:realtime-prune scripts and wire into CI
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(security): consolidate crypto primitives into @sim/security
Move general-purpose crypto primitives out of apps/sim into the
@sim/security package so both apps/sim and apps/realtime can share them.
@sim/security exports (all pure, dependency-free):
./compare safeCompare (constant-time HMAC-wrapped equality)
./encryption encrypt/decrypt (AES-256-GCM, iv:cipher:tag format)
./hash sha256Hex
./tokens generateSecureToken (base64url)
Migrate apps/sim call sites to use these + @sim/utils helpers:
crypto.randomUUID() -> generateId() from @sim/utils/id
createHash('sha256').digest -> sha256Hex
timingSafeEqual on hashed hex -> safeCompare
new Promise(setTimeout) -> sleep from @sim/utils/helpers
No behavior change: encryption format, digest output, and token
length are preserved exactly.
* refactor(copilot): use toError in remaining otel/finalize sites
Replace the last two `error instanceof Error ? error : new Error(String(error))`
patterns with toError from @sim/utils/errors. Completes the sweep of clean
candidates — no behavior change.
* refactor(security): consolidate HMAC-SHA256 primitives into @sim/security
Adds hmacSha256Hex and hmacSha256Base64 to @sim/security/hmac and migrates
15 webhook providers plus 5 other hot paths (deployment token signing,
outbound webhook requests, workspace notification delivery, notification
test route, Shopify OAuth callback) off bare `createHmac` calls. Secret
parameter accepts `string | Buffer` to cover base64-decoded Svix-style
secrets (Resend) and MS Teams' HMAC scheme. AWS SigV4 signing in S3 and
Textract tools intentionally retains direct `createHmac` usage — its
multi-step key derivation chain doesn't fit a generic helper.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(packages): post-audit test + packaging polish
- Add safeCompare unit tests (identity, length mismatch, hex-nibble diff).
- Add Buffer-secret cases to hmac tests to lock in Svix/MS-Teams contract.
- Declare `reactflow` as a peerDependency on @sim/workflow-types — only used for type imports.
- Add a barrel export to @sim/workflow-persistence for consumers that prefer package-level imports; subpath exports retained.
- Document the data-field invariant in load.ts for loop/parallel subflow patching.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(realtime): address PR review feedback
- Remove redundant SOCKET_PORT=3002 env from Dockerfile runner stage
(env.PORT already defaults to 3002 via zod schema).
- Reorder PORT fallback so an explicitly-set SOCKET_PORT wins over
the schema default for PORT; keeps SOCKET_PORT functional as an
override instead of dead code.
- Add dedicated type-check CI step for @sim/realtime so TS errors
surface pre-deploy (the Dockerfile runs source TS via Bun and has
no implicit build-time type check).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(realtime): remove unused SOCKET_PORT env var
SOCKET_PORT has lived in the socket server since the June 2025 refactor
but was never actually set in any deploy config — docker-compose.prod,
helm values/templates, .env.example, and docs all use PORT or the 3002
default exclusively. No self-hoster was ever pointed at SOCKET_PORT, so
removing it is safe.
Simplifies realtime port resolution to `env.PORT` (zod-validated with a
3002 default) and drops the orphaned sim-side schema entry.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Waleed Latif <walif6@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(docs): update simstudio.ai URLs to sim.ai in SSO docs
* improvement(docs): remove plan defaults table from data retention docs
* improvement(docs): consolidate self-hosting info at bottom of enterprise docs
* improvement(docs): reduce callout and FAQ overuse in enterprise docs
* improvement(docs): restore FAQs and genuine-gotcha callouts
* fix(deps): bump drizzle-orm to 0.45.2 (GHSA-gpj5-g38j-94v9)
Resolves Dependabot alert #98. Drizzle ORM <0.45.2 improperly escaped
quoted SQL identifiers, allowing SQL injection via untrusted input
passed to APIs like sql.identifier() or .as().
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(mcp): adopt native SDK types after @modelcontextprotocol/sdk 1.25.3 bump
Replace hand-written schema/annotation shapes with the SDK's exported
Tool, JSONRPCResultResponse, and Tool['annotations'] types so changes
upstream flow through automatically.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(types): use drizzle $inferSelect for row types
Replace hand-written interfaces that duplicated schema shape with
typeof table.$inferSelect aliases for webhook, workflow, and
workspaceFiles rows. Also simplify metadata insert/update to use
.returning() instead of field-by-field copies.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(uploads): fall through to INSERT if restore-deleted row races a hard delete
If a hard delete races between the initial SELECT and the restore UPDATE,
.returning() yields no row. Previously the function would return undefined
and silently violate the Promise<FileMetadataRecord> contract. Now the
function falls through to the INSERT path, which already handles
uniqueness races via the 23505 catch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(uploads): align metadata.ts with global standards
Replace dynamic uuid import with generateId() per @sim/utils/id
convention, narrow the error catch off `any`, and convert the inline
comment to TSDoc.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* 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