Commit Graph
644 Commits
Author SHA1 Message Date
Theodore Li 1ff445ae80 feat(codepipeline): add AWS CodePipeline integration with tools and block (#4945)
* feat(codepipeline): add AWS CodePipeline integration with tools and block

* fix(codepipeline): address review feedback on input coercion and error statuses

* chore(hooks): restore use-inline-rename onSave type accidentally swept into previous commit
2026-06-10 14:51:24 -04:00
Waleed 3cedac8e82 fix(security): authz, IDOR, and abuse-prevention fixes (#4944)
* fix(knowledge): require write access for batch chunk operations

The PATCH /api/knowledge/[id]/documents/[documentId]/chunks handler
performs enable/disable/delete operations but authorized callers with
only read-level access (checkDocumentAccess). This let read-only
workspace members destroy or disable indexed chunks.

Switch to checkDocumentWriteAccess (write/admin required), matching the
sibling POST/PUT/DELETE chunk mutation endpoints.

* fix(env): restrict decrypted workspace env vars to secret admins

GET /api/workspaces/:id/environment returned decrypted workspace
environment variables to any member, including read-only collaborators,
leaking API tokens, database URLs, and other secrets.

Mask workspace variable values for non-admin viewers while preserving
the variable names, so editor autocomplete and conflict detection keep
working. A value is revealed only when the caller is a credential admin
of that key, or — for legacy keys with no per-secret ACL — holds
workspace admin permission. This mirrors the per-key edit gating already
enforced by PUT/DELETE: if you can administer a secret, you can read it.

Personal variables and execution-time resolution are unchanged.

* fix(files): block cross-tenant deletion via client-controlled context

POST /api/files/delete trusted a client-supplied `context`, letting any
authenticated user delete another tenant's file by naming an arbitrary
key with `context: "og-images"`. verifyFileAccess() short-circuited the
three public contexts (profile-pictures, og-images, workspace-logos) to
`true` before any ownership/requireWrite check.

- Derive the storage context strictly from the trusted key prefix in the
  delete route; reject a supplied `context` that disagrees with the key.
- Gate the public-context short-circuit to reads only. Destructive ops
  (requireWrite) now prove ownership via verifyPublicAssetWriteAccess:
  workspace-logos require write/admin on the bound workspace,
  profile-pictures require an exact owner match, og-images always deny.

Reads of public assets are unchanged.

* fix(telegram): verify X-Telegram-Bot-Api-Secret-Token on inbound webhooks

Telegram triggers accepted any forged update from anyone who knew the
webhook URL path: verifyAuth was a no-op that always returned null, and
setWebhook registered no secret_token.

Generate a per-webhook secret in createSubscription, register it with
Telegram as secret_token, and persist it to providerConfig. verifyAuth
now fails closed — rejects when no token is configured, when the
X-Telegram-Bot-Api-Secret-Token header is absent, or when it does not
match via constant-time safeCompare.

* fix(security): pin DNS for Agiloft directExecution and Grafana update tools

The Agiloft directExecution tools (read/create/search/update/delete/lock/
saved_search/select/get_choice_line_id/remove_attachment/attachment_info)
and the Grafana update_dashboard/update_alert_rule postProcess hooks issued
outbound HTTP to a fully user-controlled host (instanceUrl/baseUrl) via the
global fetch(), guarded only by the synchronous validateExternalUrl() — which
never resolves DNS, so a hostname resolving to an internal/reserved IP passed
validation (SSRF).

Route all of these through the codebase's standard SSRF-safe path:
- Agiloft: moved executeAgiloftRequest into utils.server.ts where the existing
  pinned helpers live. It now resolves+validates the instance URL once and pins
  every hop (login, operation, logout) to that IP via secureFetchWithPinnedIP.
  The 11 tool configs now import it from utils.server; URL builders stay in the
  client-safe utils.ts.
- Grafana: the postProcess POST/PUT now uses validateUrlWithDNS +
  secureFetchWithPinnedIP, matching the already-pinned initial GET.

This completes the Agiloft SSRF pinning started in #4639 (which covered the
attach/retrieve API routes) by closing the directExecution path, and extends
the same guard to the Grafana update tools.

* fix(api): enforce workspace allowPersonalApiKeys policy on v1 surface

The external v1 API authenticated API keys without evaluating the
per-workspace allowPersonalApiKeys setting, so a personal API key could
read and mutate a workspace's resources (workflows, tables, files,
knowledge, logs) even when the workspace had explicitly disabled personal
keys. The same control is already enforced on the workflow-execution
surface.

Enforce the policy in checkWorkspaceScope (covering validateWorkspaceAccess
too): reject personal keys with 403 when the workspace has
allowPersonalApiKeys=false. checkWorkspaceScope becomes async; all v1
route callsites updated to await it.

* fix(billing): close usage-cap admission race with atomic reservation

The server-side usage-limit gate read already-recorded cost, but cost is
only written when an execution finishes. A burst of concurrent executions
all observed the same pre-burst usage, all passed the cap, and all ran —
collectively spending far past the limit before any cost landed in the
ledger (free-tier abuse / hard-cap defeat). manual/chat triggers also skip
rate limiting, removing the only throttle.

Add an atomic check-then-reserve admission step (Redis Lua) that bounds
in-flight, un-costed executions per billing entity by both a per-plan
concurrency cap and remaining usage headroom, so recordedUsage +
reservedSlots * estimate <= limit always holds. The slot is released at
execution completion via LoggingSession (skipped on pause; TTL self-heals
crashes). Runs for all trigger types, covering the previously-unthrottled
manual/chat paths.

Fails open when billing is disabled or Redis is unavailable, matching the
rate limiter — a Redis blip can't turn into an execution outage, and the
recorded-usage gate still runs.

* fix(workflows): validate folderId belongs to workflow's workspace on create/update/reorder

Reject a folderId that references a folder in a different workspace (or
an archived/non-existent folder) before writing it to workflow.folderId.
Previously create, update, and reorder only checked workspace permission
on the workflow and the folder's lock status, never that the folder lived
in the workflow's own workspace, allowing a dangling cross-workspace
folder reference.

Adds isFolderInWorkspace/assertFolderInWorkspace + FolderNotFoundError to
@sim/workflow-authz (mirroring assertTargetFolderMutable in the duplicate
path), enforced in performCreateWorkflow, performUpdateWorkflow, and the
reorder route. Invalid folders now return 400.

* fix(folders): validate parentId against workspace on create/update/reorder

Folder write endpoints accepted a caller-supplied parentId and persisted it
without verifying the parent existed in the same workspace, and the create and
reorder paths had no cycle guard. A workspace member with write access could
reparent a folder to a foreign-workspace folder, a non-existent id, or (via
reorder) into a cycle, hiding the folder and its workflows from all members.

- performCreateFolder: reject self-parenting and validate the parent exists in
  the workspace and is not archived (mirrors the duplicate route).
- performUpdateFolder: add the same workspace/archived parent check alongside
  the existing circular-reference guard.
- folders/reorder: validate every target parent against the workspace, detect
  cycles in the resulting parent graph (catches batch cycles), and normalize
  falsy parentId to null to prevent orphaning.

Adds tests for cross-workspace parent rejection and batch-cycle rejection.

* chore(knowledge): drop non-TSDoc inline comments from chunks route

* fix(webhooks): fail closed when HMAC signing secret is not configured

Inbound webhook signature verification failed open for HMAC providers
(GitHub, Intercom, Jira, JSM, Confluence, Cal.com, Notion, Greenhouse,
Typeform, Fireflies, Circleback): when no signing secret was stored,
verifyAuth returned null and the workflow executed on a fully
attacker-controlled body. Reject these deliveries with 401 instead,
matching the fail-closed Stripe/WhatsApp/Vercel providers.

Run provider reachability/verification handshakes (Notion
verification_token, Grain/Intercom ping) ahead of auth so the
pre-secret setup handshake still completes — those return a canned 200
without executing the workflow, and real event payloads fall through to
fail-closed verification.

Update the trigger secret-field copy to state the secret is required
for deliveries to be accepted (was misleadingly marked optional).

* style(files): trim verbose inline comments on delete authorization fix

* fix(auth): close account-enumeration oracle on email sign-up

The custom before-hook pre-check threw a distinguishing
422/USER_ALREADY_EXISTS for already-registered emails, letting an
unauthenticated attacker enumerate accounts — defeating better-auth's
own OWASP enumeration protection (active under requireEmailVerification).

Remove the pre-check and rely on better-auth's generic duplicate-sign-up
response, wiring:
- onExistingUserSignUp: notify the real account owner out-of-band,
  mirroring the privacy-preserving forget-password flow.
- customSyntheticUser: include admin (role/banned/banReason/banExpires)
  and Stripe (stripeCustomerId, billing-gated) user fields so the fake
  response shape is byte-identical to a real new-user response.

Adds an ExistingAccountEmail template + 'existing-account' subject.

* style(tools): drop non-TSDoc inline comments from Grafana/Agiloft SSRF tools

* chore(api): trim extraneous inline comments in v1 logs/files routes

Remove a redundant size annotation and two verbose multi-line
materialization comments whose intent is already clear from the code.
Load-bearing comments (race-condition and key-translation notes) kept.

* fix(billing): exclude table-cell dispatch from admission reservation

Table-cell dispatch is row-bounded, async rate-limited, and already
surfaces a graceful usage state. Applying the in-flight concurrency
reservation there turned its 429 into a hard cell error on a normal
>15-concurrent-cell run (only 402 was handled gracefully). Skip the
reservation for that surface via a new skipConcurrencyReservation option
(the usage-cost cap is still enforced), and tidy the reservation comments
to TSDoc.

* fix(chat): rate-limit and constant-time password auth for public chats

Password-protected public chat (POST /api/chat/[identifier]) had no
throttling on the password check and compared with a non-constant-time
!==, allowing unlimited brute-force and per-character timing leaks.

- Add per-IP rate limiting (10 / 15min) to the password branch of
  validateChatAuth, mirroring the OTP/SSO endpoints; return 429 with
  Retry-After. Only explicit unlock attempts consume tokens — message
  sends carry no password and ride the auth cookie.
- Replace password !== decrypted with safeCompare.
- Fails open on rate-limiter storage errors; no availability regression.

* fix(security): cap JSON request body size and gate public chat endpoint

The shared parseJsonBody helper (behind parseRequest, used by nearly
every contract route) read request bodies with no size limit, buffering
the full body into memory before validation. The unauthenticated public
deployed-chat endpoint reached this sink with no admission gate, enabling
an anonymous memory-exhaustion DoS.

- parseRequest/parseJsonBody now enforce a byte cap via a size-limited
  stream read (content-length precheck + streamed cap), returning 413.
  Default is API_MAX_JSON_BODY_BYTES (50 MB), overridable per route via
  maxBodyBytes. Decoding uses TextDecoder to match request.json() BOM
  handling.
- Public chat POST is wrapped with the admission gate (tryAdmit) and
  passes an explicit CHAT_MAX_REQUEST_BYTES (20 MB) cap.
- Chat body contract gains .max() bounds on input, password,
  conversationId, file data/name/type, and files array length.
- Admin bulk workspace import opts into a higher 100 MB cap to avoid
  regressing large multi-workflow imports.

* fix(chat): rate-limit and constant-time password auth for public chats

Password-protected public chat (POST /api/chat/[identifier]) had no
throttling on the password check and compared with a non-constant-time
!==, allowing unlimited brute-force and per-character timing leaks.

- Add per-IP rate limiting (10 / 15min) to the password branch of
  validateChatAuth, mirroring the OTP/SSO endpoints; return 429 with
  Retry-After. Only explicit unlock attempts consume tokens — message
  sends carry no password and ride the auth cookie.
- Replace password !== decrypted with safeCompare.
- Fails open on rate-limiter storage errors; no availability regression.

Reinstates the fix reverted by an intervening commit.

* fix(billing): never block a lone execution on usage headroom

The admission reservation tapered allowed concurrency by remaining usage
headroom. With under one credit of headroom left (but not yet over the
cap), floor(headroom / estimate) hit zero and rejected even a single,
zero-concurrency execution — stricter than the recorded-usage gate, which
would have allowed that last run, and with a misleading "too many
concurrent executions" message. Floor the headroom term at 1 so a lone
execution is governed only by the cost gate; concurrency above the first
slot still tapers with headroom.

* refactor(env): document workspace env masking, drop inline comments

Extract the workspace-env value masking into a TSDoc-documented
maskWorkspaceEnvForViewer helper and remove the redundant inline
comments from the GET handler and its test. No behavior change.

* refactor(env): convert PUT/DELETE authz comments to TSDoc

Move the tiered-authorization rationale for the workspace env upsert and
delete handlers into TSDoc blocks and drop the inline comments. No
behavior change.

* fix(telegram): keep legacy webhooks working via Telegram source-IP fallback

The secret-token check rejected every webhook registered before secret_token
support, breaking live triggers until re-saved. Fall back to verifying the
request originates from Telegram's published webhook IP ranges when no secret
is configured, so existing triggers keep firing with no re-save or migration
while forged updates from arbitrary hosts are still rejected. Webhooks with a
registered secret continue to use strict constant-time token verification.

* fix(chat): restore constant-time password auth and IP rate limit

A billing commit (ac565253a4) reverted the public-chat auth hardening as
collateral, leaving HEAD with a timing-oracle password comparison
(password !== decrypted) and no per-IP brute-force rate limit. Restore
safeCompare and the password-attempt rate limiter, and re-add the 429 test.

* revert(webhooks): undo trigger auth hardening pending compat plan

Reverts the Telegram inbound-token verification (3ed97a440b, 41f133a9d7)
and the HMAC fail-closed change (5b6cae9120). Production data shows ~79
live webhooks have no signing secret configured (63 GitHub, 9 Fireflies,
3 Jira, 2 Circleback, 1 Confluence, 1 Cal.com), so failing closed would
401 them. Restoring fail-open behavior until a backwards-compatible
rollout (grandfather existing secretless webhooks / migration) is designed.
Other security fixes on this branch are unaffected.

* test(chat): make RateLimiter mock a constructable class

The arrow-function mockImplementation form was not reliably constructable
in the full suite run (`new RateLimiter()` threw "is not a constructor"),
though it passed in isolation. Switch to the class-based mock used by the
sibling OTP/speech route tests.

* fix(billing): release admission slot on pre-execution aborts; cluster-safe release

Addresses PR review on the usage-cap admission reservation:

- Slot leak: the reservation taken at the end of preprocessing was only
  released when the LoggingSession finalized. The execute route's
  pre-execution exits (client cancel, workspace/API-key guards) returned
  without finalizing a session, leaking the slot until its TTL and wrongly
  throttling later runs. Release explicitly on those paths; executions that
  start are still released via session finalization.
- Release is now cluster-safe: replaced the Lua script that rebuilt the
  in-flight key from the pointer value (a key not declared in KEYS, which
  silently breaks Redis Cluster slot routing) with discrete single-key
  GETDEL + ZREM commands.

* improvement(files): log missing owner metadata distinctly on profile-picture delete deny

Per PR review: when a profile-picture delete is denied, distinguish a
missing owner record (no userId metadata) from a genuine ownership
mismatch so the fail-closed denial is diagnosable. Behavior unchanged —
both still deny.

* fix(billing): release admission slot when async enqueue fails

If queueing the background workflow job throws, no job runs and no
LoggingSession finalizes, so the admission slot reserved during
preprocessing would leak until its TTL. Release it before returning 500.

* fix(api): make body-size caps NaN-safe and raise chat input/attachment limits

- DEFAULT_MAX_JSON_BODY_BYTES and CHAT_MAX_REQUEST_BYTES now fall back to
  hardcoded defaults (50 MB / 220 MB) when the env value is missing or
  non-numeric, so a misconfig can't silently produce a NaN cap that never
  rejects.
- Raise CHAT_MAX_REQUEST_BYTES default to 220 MB to cover 15 base64 file
  attachments, and MAX_CHAT_INPUT_CHARS to 1,000,000.
- Minor: tidy use-inline-rename onSave type; drop two redundant test comments.

* fix(hooks): restore void return in useInlineRename onSave type

A prior commit changed onSave's return type from `void | Promise<unknown>`
to `undefined | Promise<unknown>`, which broke the build: callbacks that
return nothing (table-grid column rename, table header rename) infer a
`void` return, which is not assignable to `undefined`. Restore the `void`
union so both fire-and-forget and Promise-returning callbacks type-check.

* fix(billing,api): release chat reservation slot on early exit; preserve 413 on oversized import

- Chat route: preprocessExecution reserves a billing concurrency slot, but
  the post-preprocess early exits (missing workspaceId, execution-setup
  failure) returned without releasing it, leaking the slot until TTL and
  wrongly throttling later runs. Release explicitly on those paths
  (idempotent), mirroring the workflows execute route.
- Admin import route: an oversized JSON body now returns the real 413 from
  parseJsonBody instead of being remapped to a 400; invalid JSON still 400s.

* fix(icons): make Infisical icon black for contrast; regenerate docs

The Infisical mark rendered near-white on its yellow block background and
was barely visible; switch its fill from currentColor to #000000 (matching
the hardcoded-fill pattern of sibling brand icons). Sync the docs icon copy
and pick up a stale servicenow doc regeneration.

* fix(billing): release reserved slot on execute-route 503 and setup throw

After preprocessExecution reserves a billing concurrency slot, the streaming
path could exit without releasing it: the 503 return when
initializeExecutionStreamMeta fails, and any throw during stream setup (caught
by the outer handler, which only returned 500). Both left the slot held until
TTL, wrongly throttling unrelated runs. Release on the 503 path and in the
outer catch (executionId hoisted so the catch can see it; release is
idempotent and a no-op when no slot was reserved).

* fix(icons): make Linkup icon black for contrast

The Linkup mark rendered with currentColor (near-white on its block
background); switch its fill to #000000 for legibility, matching the
Infisical fix. Docs icon copy synced via generate-docs.

* fix(billing): release reserved slot if inline async job never starts

In the inline (single-process) async path, if jobQueue.startJob threw before
executeWorkflowJob ran, no LoggingSession finalized and the reserved billing
slot was held until TTL. Release it in the fire-and-forget catch (idempotent;
a no-op when the job already finalized and released). The queued-worker path
and all in-job outcomes already release via the job's LoggingSession finalize.
2026-06-10 11:38:58 -07:00
Vikhyath MondretiandCursor 6abcf82db2 feat(workflows): sim trigger, logs v2 block, toolbar renaming (#4941)
* feat(workflows): sim trigger, logs v2 block, toolbar renaming

* fix(review): bound rule queries, canonical logs params, watched-workflow SQL scoping

Code-review fixes: read the canonical workflowIds param in logs_v2 (the
serializer deletes the source pair ids), aggregate failure-rate in the DB
and switch rule windows to the indexed startedAt column, clamp rule config
to the legacy contract bounds, push no_activity watch scoping into SQL
before the LIMIT, fix the generated sim icon-map key, normalize docs
wording, and drop dead exports.

Co-authored-by: Cursor <cursoragent@cursor.com>

* address comments

* fix(review): integer rule rounding, success-gated workflow labels, display module hygiene

Second-pass review fixes: round integer rule fields so fractional input
never reaches SQL LIMIT, gate workflow-name readiness on a successful
non-placeholder load in both editor and preview (errored loads mislabeled
valid workflows as deleted), lazily read the variables store in preview
rows, move the filter-field JSON preview into the shared display module
and unexport its single-consumer helpers, and align >= boundary copy
(failure rate, error count, cooldown window) with implementation.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: sync lockfile after staging merge

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(workspace-events): keyset-paginate the no_activity subscription scan

A fixed LIMIT 500 with no ORDER BY silently starved subscriptions beyond
the cap once the global count exceeded it. The poll now pages by webhook
id so every subscription is visited each cycle; pagination bounds memory,
not total work.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(workspace-events): keyset-paginate the watched-workflow scan

The 500-row LIMIT silently and deterministically excluded high-id
workflows from no_activity coverage in watch-everything subscriptions on
large workspaces. The scan now pages by workflow id, mirroring the
subscription scan; per-workflow checks move into a helper so the
pagination loop stays flat.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(workspace-events): skip no_activity subscriptions on the execution-completion path

no_activity is poller-owned and can never fire from a completed execution,
but it passed into the rule branch and cost a pointless cooldown point-read
per subscription on the hottest path. Early-continue alongside the
workflow_deployed guard.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(sim-trigger): note failure-based alert conditions evaluate on failed runs

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(blocks): recategorize Data Enrichment as a core block

It's a Sim-native capability (registry enrichments over a managed provider
cascade, like Search), not a third-party integration. Moves it to Core
Blocks in the toolbar, out of the integrations catalog, and relocates its
docs page to blocks/ with the icon-map allowlist keeping the docs card icon.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(blocks): recategorize MySQL, PostgreSQL, SFTP, SMTP, SSH as integrations

External-system connectors with host/credential auth belong under
Integrations, not Core Blocks — consistent with MongoDB, Redis,
ClickHouse, and the other datastore integrations. They already carried
integrationType and /tools docsLinks; the regenerated docs pages turn
those previously-dangling links into real pages, and the blocks join the
integrations catalog and icon maps.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-10 11:04:12 -07:00
Waleed 62c48bfd31 improvement(tools): validate integrations, add Gong activity tools, regenerate docs (#4937)
* improvement(tools): validate integrations, add Gong activity tools, regenerate docs

* fix(servicenow): give list-attachments limit a unique subBlock id

Read Records and List Attachments shared the subBlock id 'limit', so the
single-value-per-id store could bleed the value across operations. Rename
the new list-attachments field to attachmentLimit and map it back to the
tool's limit param.

* fix(servicenow): type upload-attachment response json to fix build typecheck

secureFetchWithValidation's response.json() resolves to unknown under the
build's stricter typecheck; cast the parsed body so data.result is accessible.

* refactor(servicenow): type upload-attachment response per file-route convention

Match the SharePoint/OneDrive upload pattern: import the named
ServiceNowAttachment type from the tool's types, cast response.json() to it,
and extract specific fields with ?? null instead of passing data.result
through as an opaque unknown blob.

* fix(servicenow): remove invalid 'custom' generationType from groupBy wandConfig

'custom' is not a member of the GenerationType union, breaking the build
typecheck. No valid type fits a comma-separated field list, so drop the
wandConfig (consistent with the block's other field-list inputs).

* fix(servicenow): drop redundant hidden fileContent param from upload attachment

Per project rule, visibility:'hidden' is reserved for framework-injected
tokens, not user-supplied data. fileContent was a copied-over legacy
fallback with no caller (the block uploads via the canonical file/UserFile
path), so remove it from the tool, types, contract, and route.

* fix(dub): require a link selector for bulk update links

Bulk Update Links could run with only Update Data and no Link IDs or
External IDs, sending Dub a request with no target links and producing a
confusing API error. Guard the tool to throw a clear validation error when
neither selector is provided, and clarify the block placeholder.
2026-06-09 20:41:49 -07:00
Waleed 209501e2da feat(slack): assistant thread ops, paginated history/replies, and permalink (#4934)
* feat(slack): add assistant thread ops, paginated history/replies, and permalink

- New tools: slack_set_status, slack_set_title, slack_set_suggested_prompts
  (assistant.threads.*), slack_get_channel_history + slack_get_thread_replies
  (paginated conversations.history/replies via directExecution), slack_get_permalink
- Wire all six as Slack block operations with subBlocks, conditions, params, outputs
- Add scopes: assistant:write, im:history, mpim:read, mpim:history (+ consent labels)
- Add manifest capabilities: action_read_history, action_assistant
- Fix invite_to_conversation no_user error code; widen DM history scope hints
- Regenerate tool docs and integrations catalog

* fix(slack): correct thread parent on cursor-resume, complete DM scope hints, reject empty suggested prompts

- get_thread_replies: identify thread parent by ts === thread_ts instead of
  assuming index 0, so cursor-resumed pages (which omit the parent) no longer
  mislabel a reply as the parent
- get_channel_history + get_thread_replies: include im:history/mpim:history in
  the missing_scope hint to match the OAuth grant and DM/MPIM reads
- set_suggested_prompts: throw a clear error when no valid prompt is provided
  instead of silently calling Slack with an empty prompts array

* fix(slack): guard non-numeric limit/maxPages; drop ungranted im/mpim history scopes

- Add resolvePositiveInt helper and use it for limit/maxPages in
  get_channel_history and get_thread_replies, so a non-numeric value (e.g. from
  an LLM) no longer becomes NaN and silently disables pagination (returning an
  empty success)
- Remove im:history/mpim:read/mpim:history from the Slack OAuth grant — the Slack
  app does not have these scopes; assistant:write is the only added scope
- Drop the explanatory inline comment in get_thread_replies

* improvement(slack): move history pagination cursor/maxPages to advanced mode

Matches the existing paginationCursor convention in the block — keeps the basic
UI focused on channel + time filters while pagination internals live under
advanced.

* fix(slack): narrow set_status missing_scope hint to assistant:write

A user hitting missing_scope on set_status already has chat:write (long
granted); the scope they actually need is assistant:write, which is also what
set_title/set_suggested_prompts point to. Narrow the hint to assistant:write for
accurate, consistent guidance.
2026-06-09 18:28:27 -07:00
Waleed efa4f27006 fix(agent): unique tool ids for multi-instance tools + icon updates (#4933)
* fix(agent): resolve canonical resource id when building unique tool ids

Multi-instance agent tools (table, knowledge base, workflow) get a unique
id by suffixing the resource id (e.g. table_query_rows_<tableId>). The
suffix code read the canonical param key (tableId/knowledgeBaseId) directly
from stored params, but selector subblocks persist their value under the
subblock id (tableSelector/knowledgeBaseSelector). Tools stored in that
state never got a suffix, so two of them collapsed to the same name.

Resolve the canonical resource id from the source subblock first — mirroring
the execution-time paramsTransform — so the unique id is derived the same
way the tool actually runs. Non-destructive; no-op when the canonical key is
already present.

* test(agent): cover knowledge-base selector resolution for unique tool ids

* improvement(icons): new 1Password icon on white bg, enlarge Prospeo and Neo4j glyphs
2026-06-09 17:04:26 -07:00
+1 39418f9615 improvement(mothership): v0.2 (#4923)
* CONTRACTS

* updates

* Prompt caching

* Fix regression

* updates to prompt caching

* Prompt caching trace

* VFS updates

* Changelog and plan

* VFS update

* Improvement/mothership (#4775)

* ff

* System role in cache

* Fixes

* Add dynamic user info

* Improve tool search

* improvement(platform): workspace UI/UX overhaul + integrations catalog

Rework the workspace around the AI-workspace model: a Mothership home, a
top-level Skills route, connected-credential and integration-detail pages,
and a polished sidebar/settings surface. Replace the notifications store
with a unified toast system (provider-level dismiss/pause, countdown ring).

Integrations & catalog:
- Add a BlockMeta layer (tags + catalog templates) scoped to catalog-visible
  integrations; every catalog integration carries >=7 grounded templates.
- Rework the taxonomy: each block declares category tools|blocks|triggers.
  3rd-party services are 'tools'; first-party primitives (postgres, mysql,
  knowledge, file, search, stt/tts, image/video generators, thinking, etc.)
  are 'blocks'. Versioned blocks follow the upgrade paradigm (old hidden,
  latest in toolbar/docs).
- Generate integrations.json + tool docs canonically from block configs.

Architecture & cleanup:
- Consolidate block data extraction behind a single latest-version strategy
  (getCanonicalBlocksByCategory; version-consistent getBlockMeta).
- Unify version-suffix handling in @sim/utils/string (stripVersionSuffix /
  isVersionedType, with tests); registry, generate-docs, tools/utils, and
  integrations all route through it.
- Repair latent broken barrels, remove dead code, fix BlockMeta-related type
  errors and 5 broken docs links.

Behavior-preserving for block execution and the toolbar's tool/block listing.

* refactor(platform): remove forms, templates, and creators features

Remove three standalone features and their supporting code:
- Forms: form-deployment pages, API routes, execution path, and docs.
- Templates: the template gallery (landing + workspace) and template APIs.
- Creators: creator-profile routes and contracts.

Add a super-user permissions module (lib/permissions/super-user) and an
organizations API contract; update the audit/db/testing packages, billing,
and the session/theme providers accordingly.

* New doc setup

* Fixes

* Fixes

* Logs tools

* test(workflows): update archiveWorkflow update count after forms removal

The forms feature was removed, dropping the form-table update from
archiveWorkflow. Update the stale assertion from 8 to 7 tx.update calls.

* Add finalization on error

* Fixes

* change dev CI to bun run db:push

* Updates

Please enter the commit message for your changes. Lines starting

* Contrat update

* improvement(nested): subagents

* updates

* upgrade

* improvement(knowledge): polish tag filter dropdowns (#4816)

* improvement(logs): object storage backed tracespans (#4787)

* improvement(logs): obj storage backed tracespans

* fix storage write context

* fix tests

* address comments

* address comments

* chore(db): remove migration 0219 to regenerate after staging merge

Drops the 0219_robust_shard SQL, its snapshot, and the journal entry so the
trace-spans/cost schema migration can be regenerated on top of the latest
staging migration chain (avoids a number collision with staging's migrations).

Co-authored-by: Cursor <cursoragent@cursor.com>

* improvement(billing): accurate per-member usage via shared ledger helper

Per-member/per-user usage in the org-member routes now adds the usage_log
ledger to the currentPeriodCost baseline (which is no longer incremented),
via a shared getOrgMemberLedgerByUser helper to avoid repeating the
subscription→period→ledger lookup across the admin and member-facing routes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* regen migrations

* update migration

* address comments

* more code cleanup

* incorrect type cast

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* improvement(providers): harden OpenAI-compatible providers + add tests (#4796)

* improvement(providers): harden OpenAI-compatible providers + add tests

* fix(vllm): let tool-loop errors propagate instead of returning silent partial success

* fix(litellm): force tool_choice 'none' on final structured-output call

The deferred final call used tool_choice 'auto', so the model could emit
another tool_calls round instead of the structured answer, leaving content
stale. Use 'none' (matching vLLM/Fireworks) on both the streaming and
non-streaming final calls so the model must return the structured response.

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

* fix(providers/ollama): drop tools from post-tool streaming call

Ollama ignores tool_choice (not in its supported fields), so vLLM/Fireworks'
tool_choice:'none' guard is a no-op here. Omit tools from the final streaming
payload instead so the summarization turn can't emit dropped tool calls.

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

* fix(litellm): spread payload into deferred final call so reasoning_effort carries over

The non-streaming deferred finalPayload hand-picked fields and dropped
reasoning_effort (and any future payload field), diverging from the streaming
path which spreads ...payload. Spread payload here too for consistency.

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

* chore(providers/ollama): restore enrichment TSDoc block

Keeps parity with sibling Chat Completions providers (cerebras/mistral/xai).

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

* docs(fireworks): restore TSDoc on utils helpers

Restore the TSDoc blocks on supportsNativeStructuredOutputs,
createReadableStreamFromOpenAIStream, and checkForForcedToolUsage —
TSDoc is the codebase documentation standard and should not have been
stripped.

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

* chore(litellm): remove inline rationale comments (codebase uses TSDoc)

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

* chore(providers/ollama): drop orphaned enrichment TSDoc

The block documented a function that now lives in trace-enrichment.ts, so it
documents nothing in this file.

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

---------

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

* chore(copilot): deprecate mcp server (#4797)

* chore(copilot): deprecate mcp

* update error codes

* deprecate copilot api v1 route

* feat(integrations): hosted API keys for Findymail, Prospeo, and Wiza (#4777)

* feat(integrations): hosted API keys for Findymail, Prospeo, and Wiza

Add hosted-key support across all credit-consuming Findymail, Prospeo, and Wiza operations so Sim provides the key when a workspace has not brought its own. Register the three BYOK providers, consolidate Wiza's two-step reveal into a single polling wiza_individual_reveal op, and hide the API key field on hosted Sim for hosted operations.

* fix(integrations): harden Wiza reveal polling, soften enrichment getCost guards

Address Greptile + Cursor Bugbot review on #4777: return explicit failures from the Wiza individual_reveal poller instead of throwing (thrown errors were swallowed into a false queued success), short-circuit when the initial reveal is already terminal, tolerate transient 5xx/429 during polling, and return 0 (not throw) from Findymail getCost when the contacts/employees array is absent.

* chore(integrations): biome formatting after wiza merge resolution

* fix(wiza): type isTerminalReveal param structurally for next build typecheck

* feat(enrichments): add Findymail, Prospeo, Wiza to work-email waterfall

* feat(enrichments): add Wiza + Prospeo phone reveal to phone-number waterfall

* feat(enrichments): opportunistic identifiers + LinkedIn URL input across work-email & phone cascades

* fix(tables): reduce column header chevron size and fix sidebar shadow bleed (#4800)

* feat(slack): add install + privacy section to integration landing page (#4799)

* feat(slack): add install + privacy section to integration landing page

Adds a hand-authored, slug-keyed landing-content module (separate from the generated integrations.json so it survives regeneration) and renders an install walkthrough + privacy-policy link on integration pages when present. Also refreshes generated docs (data-enrichment entry, icon mappings, tool mdx).

* fix(landing): render privacy section independently, align CTA analytics label

* docs(landing): clarify the Slack install button is behind sign-in

* refactor(landing): bake integration landing content into generated json via docs-gen

Moves landing content (install walkthrough + privacy) out of a render-time augment and into the generation pipeline: generate-docs reads the pure-data content map and writes landingContent into integrations.json, so the page reads a single source (integration.landingContent). Canonical types live in integrations/data/types.ts.

* improvement(enrichments): align enrichments sidebar with design system (#4801)

* improvement(enrichments): align enrichments sidebar with design system

* fix(enrichments): consistent close button pattern and fix url link hover

* fix(misc): upgrade path change for new better-auth version, billing issue for workflow block agent usage (#4803)

* fix(misc): upgrade path change for new better-auth version, double-billing for workflow block agent usage

* fail loudly if stripe sub id missing

* fix(copilot): seq migration (#4804)

* chore(db): drop redundant idx_webhook_on_workflow_id_block_id index (#4809)

Removed because (workflow_id, block_id) is a left-prefix of idx_webhook_on_workflow_id_block_id_updated_at_desc, which fully covers it. The dropped index was non-unique and enforced no constraint.

* perf(copilot): read chat transcripts from copilot_messages (R+1 cutover) (#4808)

* perf(copilot): read chat transcripts from copilot_messages, not JSONB

Flip user-facing chat reads from the legacy copilot_chats.messages JSONB
array (5.7GB, 99% TOAST) to the normalized copilot_messages table via a
new loadCopilotChatMessages helper ordered by seq NULLS LAST, created_at,
id — the verified canonical order. Both chat-detail getters
(getAccessibleCopilotChat, getAccessibleCopilotChatWithMessages) now drop
the messages column from their metadata select (no more whole-array
detoast on every load) and assemble the transcript from the table after
authorization. This cascades to the copilot + mothership GET endpoints
and to resolveOrCreateChat's conversationHistory (the LLM payload).

The normalize/effective-transcript pipeline is source-agnostic
(copilot_messages.content == a JSONB array element), so transcripts are
byte-identical. Dual-write and the JSONB column stay in place as the
internal-logic source and fallback; removing JSONB writes is a later step.

Prod integrity verified before cutover: 0 messages missing, 0 NULL-seq,
0 dup keys/seq, 0 orphans, order-parity vs JSONB = 0 mismatches.

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

* test(copilot): cover auth-deny on a found row skips the messages query

Address PR review: exercise the `if (!authorized) return null` contract —
when the chat row exists but authorization fails, the getter returns null
and never issues the copilot_messages read.

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

---------

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

* fix(tables): right-align run/stop in embedded toolbar; workflow cells format like normal cells (#4806)

* fix(tables): right-align run/stop in the embedded table toolbar

Add a right-aligned `trailing` slot to ResourceOptionsBar and move the embedded
mothership table's run/stop control into it, so Filter + Sort stay left-aligned
and run/stop sits opposite on the right. No-op for the search-bearing consumers
(logs, resource list), which don't pass `trailing`.

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

* fix(tables): workflow-output cells format values like normal cells

Workflow-output columns short-circuited in resolveCellRender and rendered their
value as plain text, so a sim-resource URL / external URL / JSON / date produced
by a workflow never got the chip, favicon link, or typed formatting a normal
cell gets. Factor value formatting into a shared `resolveValueKind` helper used
by both the workflow-value branch and the plain-cell branch; the workflow branch
keeps the typewriter reveal for plain streaming text via a `typewriter` flag.

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

* fix(tables): detect resource/URL links on workflow output regardless of column type

Workflow output columns default to `json` (columnTypeForLeaf), so routing their
values through the type-based formatter (a) gated chip/URL promotion behind
`column.type === 'string'` — a URL produced by a json-typed output never became
a chip — and (b) JSON.stringify'd plain string values, adding quotes and losing
the typewriter reveal. Detect links (sim-resource chip / favicon URL) on the
value string directly for workflow outputs, falling back to the plain `value`
kind; plain cells keep the type-based formatting. Addresses Greptile P2 on #4806.

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

---------

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

* fix(icons): repair broken integration icon rendering (#4810)

* fix(icons): repair broken integration icon rendering

Two distinct bugs left integration icons broken on the /integrations page
(visible at 32-40px, hidden at the toolbar's 16px):

1. Corrupted SVG paths (Notion, Greptile, Granola, Calendly, Grafana, Bedrock):
   over-minified data dropped elliptical-arc flag digits (e.g. `A1 1 0 5.9 7`
   instead of `A1 1 0 0 0 5.9 7`); Granola's cubic stream was truncated. Browsers
   abort path parsing at the first invalid arc flag, so each rendered as a fragment
   or blank. Replaced with correct path data from canonical sources, preserving each
   icon's existing fill/gradient and bgColor.

2. Invisible glyph (Bright Data): its icon uses fill='currentColor' but bgColor was
   '#FFFFFF', and every surface forces text-white on the glyph - white-on-white.
   Changed bgColor to Bright Data's brand blue (#3d7ffc) so the white glyph reads,
   matching the white-glyph-on-brand-chip convention.

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

* fix(icons): restore Calendly dual-tone brand colors

Addresses review feedback: the previous fix replaced the broken Calendly icon
with a monochrome #006BFF path, dropping the cyan #0ae8f0 accent from the
original dual-tone mark. Restored the two-tone logo (blue + cyan) using clean,
valid path data, cropped to a tight square viewBox so it fills the chip.

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

* improvement(icons): enlarge icons, fix Zoom contrast and Quiver chip

- Zoom: glyph was blue-on-blue (#0B5CFF on #2D8CFF chip); switched to
  currentColor so it renders as a white glyph on the blue chip.
- Quiver: chip bgColor #000000 -> #FFFFFF to match the icon's near-white box,
  and enlarged the mark slightly (viewBox crop).
- Enlarged (tightened viewBox, verified no clipping): RevenueCat, Prospeo,
  Granola, Firecrawl, Enrich.so, and the AWS icons (RDS, DynamoDB, SQS,
  CloudFormation, Athena, CloudWatch, SES, Bedrock, S3).
- ZoomInfo left unchanged: it is a full red rounded-square logo that already
  fills its frame, so a crop would clip it.

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

* fix(icons): use Bright Data wordmark on white chip; repair Circleback

- Bright Data: replaced the flame glyph with the official two-tone 'bright data'
  wordmark (provided asset), centered in a symmetric viewBox. Reverted the chip
  bgColor from #3d7ffc to #FFFFFF since the blue wordmark is invisible on a blue
  chip (the wordmark is designed for a light background).
- Circleback: a minifier had rounded the pattern's image scale to scale(0),
  collapsing the embedded logo to zero size (invisible). Restored the correct
  scale (1/280 = 0.00357142857) so the C. mark renders.

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

* fix(docs): sync Quiver block color card to white chip

Reflects the Quiver bgColor change (#000000 -> #FFFFFF) in the docs block info card.

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

* improvement(icons): enlarge AWS/Cloudflare/Dagster icons, fully white Zoom

- Enlarged (tighter viewBox, render-verified, no clipping): Cloudflare, Dagster,
  and the red AWS icons AWS IAM, Identity Center, Secrets Manager, SES, STS.
  Identity Center was anomalously small (filled ~32% of its frame); the group is
  now sized consistently (~80% fill).
- Zoom: the camera lens triangle was still #0B5CFF (blue-on-blue); switched it to
  currentColor so the whole camera renders white on the blue chip.

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

* docs(wiza): consolidate individual reveal into a single operation

Merges the separate Start/Get Individual Reveal operations into one Individual
Reveal operation in the Wiza docs and integrations data (operationCount 5 -> 4).

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

* improvement(icons): size remaining AWS icons to match the set (~80% fill)

Bring RDS, DynamoDB, SQS, CloudFormation, Athena, CloudWatch and S3 up to the
same ~80% fill as the AWS IAM/Identity Center/Secrets Manager/SES/STS group, so
all AWS icons are visually consistent. Bedrock left as-is (already ~92% fill).

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

* fix(icons): use Bright Data flame mark, enlarge ZoomInfo

- Bright Data: the full 'bright data' wordmark was illegible at chip size.
  Replaced with just the flame-'i' brand mark (blue #4280f6 on the white chip),
  centered.
- ZoomInfo: cropped the viewBox toward the white 'Zi' so it's larger; the red
  rounded-square background still fills the chip.

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

* improvement(icons): enlarge CrowdStrike icon

The falcon mark sat small in its chip because the icon used a wide 768x500
viewBox (letterboxed in the square chip). Switched to a square viewBox centered
on the mark so it fills ~80%, consistent with the other icons.

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

---------

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

* fix(tables): serialize schema mutations to prevent parallel column clobber (#4812)

* Make workflow description nullable

* fix(tables): serialize schema mutations to prevent parallel column clobber

* fix(tables): load workflow outside schema lock; use DbOrTx for getTableById

* fix(tables): scale idle timeout in updateColumnType to avoid aborting large type changes

* fix(tables): skip stale remap types when workflowId changes concurrently

* fix(tables): scale idle timeout in updateColumnConstraints for large tables

* fix(wait): resume live/draft async waits and preserve cell context on chained waits (#4814)

* Make workflow description nullable

* fix(wait): resume live/draft async waits and preserve cell context on chained waits

* improvement(knowledge): polish tag filter dropdowns

* improvement(knowledge): soften filter section labels

* improvement(knowledge): soften list filter labels

* fix(security): harden SSO domain registration, webhook path isolation, and CSV export (#4813)

* fix(security): harden KB file access, SSO domain registration, webhook path isolation, env secrets, and CSV export

* fix(sso): scope domain conflict query with indexed lower(domain) filter

Address PR review: avoid a full-table scan on every SSO provider
registration by filtering candidate rows in SQL with
lower(domain) = <normalized>, keeping the in-memory ownership check.
Also tighten the normalizeSSODomain TSDoc.

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

* chore: condense env route security comments

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

* icons update

* chore(security): tighten inline comments in CSV export and KB file authorization

Condense verbose comment blocks to concise TSDoc/single-line form; no behavior change.

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

* fix(security): validate internal serve origin in KB file authorization

Replace the bypassable isInternalFileUrl substring check in resolveInternalKbKey
with an origin allow-list (base URL, internal API base URL, TRUSTED_ORIGINS).
A crafted external host whose path is /api/files/serve/<victim-key> no longer
resolves to the victim key. Relative same-origin URLs are unaffected.

* style(sso): use idiomatic sql lower() comparison for domain conflict query

Match the repo's prevailing `sql`lower(col) = value`` idiom for the
case-insensitive SSO domain conflict lookup.

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

* fix(security): align workspace env admin gate with hasWorkspaceAdminAccess

Use the same admin check the secrets UI uses (owner, admin permission, or
org-admin) so owners and org-admins are not wrongly denied their own decrypted
workspace secrets, while read-only members remain restricted to names only.

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

* fix(sso): rely on lower(domain) match for conflict detection, drop dead in-memory recheck

Address PR review: the SQL `lower(domain) = <normalized>` predicate already
excludes rows that the in-memory `normalizeSSODomain(...) === domain` recheck
claimed to catch, making that recheck dead/misleading code. Match on the
canonical lower-cased domain and filter purely by ownership. Malformed legacy
values (wildcards, schemes, ports) never match an email domain at sign-in, so
excluding them is not a gap. Test DB mock now applies the lower() predicate so
the casing-variant case is genuinely exercised.

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

* fix(security): scope webhook deploy path conflict to active webhooks

findConflictingWebhookPathOwner omitted the isActive filter that the
runtime dispatcher (findAllWebhooksForPath) applies, so an inactive but
non-archived webhook from another workflow (e.g. after undeploy or
failure auto-disable) would permanently block any new deployment on that
path even though it never receives deliveries. Align the guard with the
runtime isActive + archivedAt filter; the earliest-owner runtime check
remains the authoritative cross-tenant protection. Also trims verbose
TSDoc on the webhook path-isolation helpers.

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

* fix(security): exclude archived workflows from webhook deploy path conflict

findConflictingWebhookPathOwner now joins workflow and filters
isNull(workflow.archivedAt), matching the runtime dispatcher
(findAllWebhooksForPath). A webhook on an archived workflow can never
receive deliveries at runtime, so it must not block legitimate path reuse
with a 409.

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

* fix(security): anchor KB file ownership to earliest document in any state

A KB file's owner is now the earliest document referencing its key regardless of
state (active/archived/deleted/excluded); access is granted only when that owning
document is still active. Closes the residual where an attacker could plant an
active document to claim a file whose original document was archived or deleted.

* updated greptile icon

* revert(security): drop KB file authorization changes

Reverts the knowledge-base file-access work (origin-pinning / owner-pinning /
origin allow-list in verifyKBFileAccess) and its test. The other hardening fixes
(SSO domain registration, webhook path isolation, workspace env secrets, CSV
export) are unchanged. apps/sim/app/api/files/authorization.ts is restored to its
origin/staging baseline.

* fix(sso): treat caller's own user-scoped provider as owned during conflict check

Self-hosters often register SSO user-scoped via the CLI script (no
SSO_ORGANIZATION_ID). If they later enable organizations and reconfigure the
same domain org-scoped through the UI, the conflict check previously treated
their own user-scoped row as another tenant's and returned a misleading 409.
Recognize the caller's own user-scoped provider as owned so that migration is
allowed, while still blocking another user's or another org's domain.

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

* revert(security): remove workspace-env admin gate

Defer to a credential-based access model (separate change). Restores
GET /api/workspaces/[id]/environment to main behavior and removes the test.

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

* refactor(security): consolidate webhook path-collision check into one helper

Extract findConflictingWebhookPathOwner to lib/webhooks/utils.server.ts as
the single source of truth for cross-tenant path-collision detection, used by
both webhook creation paths (deploy sync and the manual /api/webhooks route).

This also repairs two latent issues in the manual route's previous inline
check, which queried with limit(1) and only webhook.archivedAt:
- limit(1) inspected one arbitrary row, so a same-workflow row could mask a
  foreign collision (false negative). The shared helper scans all matching
  rows.
- It omitted isActive/workflow.archivedAt, so inactive or archived-workflow
  webhooks (which never receive deliveries) permanently blocked path reuse.
  The helper mirrors the runtime dispatcher's filter.

Same-workflow webhook reuse for upsert is now a separate, explicit lookup.

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

---------

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

* fix(security): block private/reserved IPs for hosted 1Password Connect SSRF (#4818)

* fix(security): block private/reserved IPs for hosted 1Password Connect SSRF

* test(security): use real isPrivateOrReservedIP and cover IPv6 edge cases

* improvement(integrations): validate and expand devin, cursor, and greptile (#4820)

* improvement(integrations): validate and expand devin, cursor, and greptile

- devin: fix missing org_id path segment on all session endpoints, add 7 session sub-resource tools (list messages/attachments, get/append/replace tags, archive, terminate), pagination, and is_archived output
- cursor: add get_api_key_info, list_models, list_repositories tools
- greptile: align block and docs
- normalize array outputs to default [] and tighten types

* refactor(cursor): simplify list_repositories v2 array normalization

Collapse the redundant `?? []` + `Array.isArray` double-guard into a
single Array.isArray check, per PR review feedback.

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

* fix(devin): scope session-tag mapping to tag ops and normalize array tag inputs

- Only map sessionTags into the tools tags param for append/replace operations,
  preventing stale sessionTags state from clobbering create_session tags
- Fall back to a wired tags value when sessionTags is empty for tag operations
- Normalize tag inputs (string or wired string[]) via normalizeTags so array
  values from other blocks no longer throw on .split

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

* fix(cursor): restore base64 file data in legacy download_artifact metadata

The legacy CursorBlock exposes only content + metadata (no v2 file
output), so metadata.data was the only way legacy-block workflows could
access downloaded artifact bytes. Restore the base64 data field and
document it in the outputs/type instead of dropping it.

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

* fix(devin): coerce terminateArchive to archive flag for boolean-wired input

* docs(integrations): regenerate tool docs for new devin and cursor operations

---------

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

* fix(search-replace): don't auto-navigate when content edits invalidate the active match (#4819)

* fix(search-replace): don't auto-navigate when content edits invalidate the active match

* fix(search-replace): clear afterReplaceIndexRef on apply failure and zero matches

* fix(search-replace): remove duplicate setActiveSearchTarget(null) on close

* fix(search-replace): move afterReplaceIndexRef write inside handleApply past the guard

* fix(search-replace): auto-navigate when hydration resolves with no prior active match

* chore(search-replace): remove inline comments

* fix(search-replace): revert !activeMatchId guard that caused immediate re-navigation after deselect

* improvement(enrichments): limit company-info to fields both providers return (#4817)

Hunter's company dataset returns null industry/foundedYear for many large companies (verified against the live API for Microsoft, Amazon, Google), so under the first-non-empty-wins cascade those columns appeared inconsistently across rows. Limit company-info outputs to employee count and description — the fields Hunter and PDL both reliably return — so every row is consistent. employeeCount is a string so Hunter's range bucket and PDL's exact count share the column.

* fix(files): don't reject external URLs containing '..' in file parse validation (#4821)

* fix(files): don't reject external URLs containing '..' in file parse validation

The file block's file_fetch operation rejected any external URL whose path
contained '..' (e.g. Slack files-pri slugs with a literal '...') with
'Access denied: path traversal detected'. Traversal checks only apply to
local paths — external http(s) URLs are fetched with SSRF protection
downstream and are never resolved against the filesystem, so they now
short-circuit as valid. Internal /api/files/serve/ URLs keep full traversal
protection.

* test(files): fix external-URL assertion to handle undefined error

* test(files): assert success explicitly in external-URL traversal test

* fix(files): keep traversal protection for https URLs matching internal serve paths

* feat(google-sheets): add row filtering to read with numeric operators (#4822)

* feat(google-sheets): add row filtering to read with numeric operators

Adds client-side row filtering to the Google Sheets read (v2) operation.
Filter the returned rows by a header column using text operators
(contains, not_contains, exact, not_equals, starts_with, ends_with) and
numeric/ordering operators (gt, gte, lt, lte). Filtering lives in a pure,
unit-tested helper (filterSheetRows) and runs over the fetched read range;
an optional `filter` output reports whether the column was found and how
many rows matched.

Also hardens the surrounding tools:
- trim spreadsheetId in write/update/append URL builders (matches read)
- URL-encode the v1 read default range
- expose valueInputOption for the update operation in the block

Backwards compatible: with no filter requested, read output is byte-
identical and the `filter` field is omitted. The filterMatchType union is
widened additively (4 -> 10 values).

* fix(google-sheets): correct filter metadata for missing column and header-only sheets

- matchedRows is now 0 (not totalRows) when the filter column is not found,
  so it no longer contradicts applied=false / columnFound=false
- columnFound now reflects an actual header lookup for empty/header-only
  sheets instead of being hardcoded true
- add tests covering header-only and empty sheets with present/absent columns

* fix(selectors): fetch all pages for paginated dropdown list routes (#4823)

* fix(selectors): fetch all pages for paginated dropdown list routes

Dropdown selectors fetched only the first page of paginated provider
APIs, silently hiding results past page one. Add bounded server-side
draining to the list routes across Microsoft Graph, Google, Notion,
Atlassian, Linear, AWS CloudWatch, and offset/token REST APIs, plus a
shared client-side drain cap in the selector hook. Response shapes,
stored values, and tool execution are unchanged; CloudWatch list tools
still honor a caller-supplied limit. Also fixes the Word file picker
that was searching for .xlsx files.

* fix(selectors): harden JSM and Monday pagination draining

- JSM service-desk/request-type drains advance `start` by the actual row
  count returned (not the fixed page size) and stop on an empty page, so a
  short non-final page can't skip items.
- Monday boards drain now checks `response.ok` per page, surfacing a
  mid-drain HTTP failure instead of treating it as an empty final page and
  returning a partial 200.

* docs(selectors): clarify JSM drain advances start by actual row count

The offset-advancement fix (advance `start` by the rows returned, not the
fixed page size) landed in 7b19788a8; update the TSDoc to match so it no
longer reads as advancing by `limit`.

* fix(selectors): drain fetchPage in direct fetchList callers

Making `fetchList` optional left three direct callers (outside the
useSelectorOptions hook) calling it unguarded, which broke the build's
type check. Route them through a shared `loadAllSelectorOptions` helper
that uses `fetchList` when present and otherwise drains `fetchPage`.
This also prevents a regression: `confluence.spaces` / `knowledge.documents`
now paginate via `fetchPage` only, and these callers (search/replace,
value resolution) would otherwise have silently returned no options.

* chore(selectors): rename MAX_PAGE_PAGES to MAX_NOTION_PAGES for readability

* fix(sso): re-check domain conflict before write and reject IP-address domains (#4825)

* improvement(copilot): make copilot_messages the sole transcript store, remove JSONB dual-write (#4826)

Stop writing/reading the legacy copilot_chats.messages JSONB column now that
reads are cut over to copilot_messages. Make appendCopilotChatMessages the
primary write (throws on failure instead of swallowing), repoint peripheral
readers (workspace VFS, chat cleanup, data drains, fork, superuser import) to
copilot_messages, and persist the assistant turn inside finalizeAssistantTurn's
transaction so it commits atomically with the stream-marker clear. The column
itself is dropped in a follow-up migration after this bakes.

* feat(tables): expand filter operators (not-contains, starts/ends-with, not-in, empty) (#4827)

Add does-not-contain ($ncontains), starts-with ($startsWith), ends-with
($endsWith), not-in-array ($nin, previously executed server-side but unexposed
in the UI), and is-empty/is-not-empty ($empty) filter operators end-to-end —
SQL builder, condition types, query-builder converters/constants, the filter
UI, the Table tools/block descriptions, and docs.

Also fix correctness bugs in the filter builder surfaced by the wider operator
set:
- Same-column AND rules (e.g. age > 18 AND age < 65, or name startsWith 'A'
  AND name endsWith 'Z') silently overwrote each other because the AND group
  was keyed by column name. They now merge into one operator object, which
  also makes Filter -> rules -> Filter round-trip losslessly for multi-operator
  columns.
- $nin values were not split into an array like $in, and textual-match values
  like "123" were numeric-coerced (breaking the ILIKE path).
- A non-boolean $empty operand from the raw API silently inverted the check; it
  now coerces 'true'/'false' strings and otherwise returns a 400.

* improvement(copilot): stop persisting tool-call result outputs in transcripts (#4829)

Opening a Mothership task could take many seconds because a single persisted
assistant message in copilot_messages.content can reach hundreds of MB, almost
entirely inside contentBlocks[].toolCall.result.output (e.g. a get_workflow_logs
or run_workflow result). The DB query is ~2ms; the cost is detoasting that
payload, shipping it to the browser, and parsing it.

These outputs are dead weight on the Sim side: they are never rendered (the
thread shows only tool name/title/status) and never replayed to the model (the
upstream copilot service owns conversation memory). So drop result.output before
it is persisted, keeping result.success/error plus the tool metadata.

- add stripToolResultOutput() in persisted-message.ts
- apply it in messages-store toRow (covers every write path) and in
  loadCopilotChatMessages (existing rows render fast on read)

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

* feat(providers): add Together AI, Baseten, and Ollama Cloud model providers (#4830)

* feat(providers): add Together AI, Baseten, and Ollama Cloud model providers

* fix(providers): guard Ollama streaming fast-path with hasActiveTools

Match Together/Baseten/Fireworks: when tools are supplied but all are
filtered out (usageControl 'none'), take the single streaming call instead
of an extra non-streaming round-trip.

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

* fix(providers): filter non-chat model types from Together model list

* refactor(providers): dedupe Ollama Cloud upstream schema

ollamaCloudUpstreamResponseSchema was byte-for-byte identical to
ollamaUpstreamResponseSchema (both /api/tags endpoints return the same
{ models: [{ name }] } shape). Drop the duplicate and reuse the shared schema.

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

---------

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

* fix(knowledge): calendar view sync, deduplicate popover animation classes, type-safe filter cast

* cleanup(knowledge): remove TRIGGER_BORDER_CLASS duplication, inline displayLabel, drop enabledFilterParam alias

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com>

* feat(blocks): add BlockMeta to Quiver and Linq; fix invalid block config fields; update skills

Block fixes:
- Add QuiverBlockMeta (tags + 3 templates: icon generator, diagram creator, vectorizer)
- Fix QuiverBlock: remove invalid tags field from BlockConfig, IntegrationType.Design →
  IntegrationType.AI (Design doesn't exist in the enum)
- Fix GreptileBlock: remove invalid tags field from BlockConfig,
  IntegrationType.DeveloperTools → IntegrationType.DevOps
- Fix LinqBlock: remove invalid tags field from BlockConfig (tags belong only in BlockMeta)

Skills:
- add-block: add dedicated BlockMeta section with structure, rules, and registration
  pattern; add BlockMeta checklist items
- add-integration: add BlockMeta to block structure template, add rules clarifying
  that tags must NOT appear on BlockConfig and integrationType must be a valid enum
  value; update registry snippet to include blocksMeta; add checklist items

* fix(integrations): fix category dropdown by defining missing LANDING_INTEGRATIONS_DATA_PATH and regenerating integrations.json

The staging merge introduced landing-content.ts but forgot to define
LANDING_INTEGRATIONS_DATA_PATH in generate-docs.ts, causing the script
to crash before writing integrations.json.

The stale JSON had integrationTypes (plural array) from an older script
version, while the Integration type and workspace UI both read
integrationType (singular string) — so ALL_CATEGORY_SECTIONS bucketed
to undefined and the category filters never appeared in the dropdown.

Fixed by adding the missing path constant and re-running the generator.
integrations.json now has 192 entries with the correct integrationType field.

* fix(sidebar): restore resize handle on all pages

commit 3109104582 wrapped the resize handle in {(isCollapsed ||
isOnWorkflowPage) && ...} and added a useEffect that resets sidebar
width to SIDEBAR_WIDTH.MIN whenever the user navigates away from a
workflow page. Together these made the sidebar non-resizable on Tasks,
Tables, Knowledge Base, and every other non-workflow page.

Restore the staging behavior: always render the resize handle and
remove the effect that forced the width reset on page transitions.

* fix(sidebar): match staging onKeyDown and tabIndex on resize handle

The resize handle was still conditionalizing onKeyDown and tabIndex
on isCollapsed, blocking keyboard accessibility of the separator role
when expanded. Staging always attaches both unconditionally.

onKeyDown={isCollapsed ? handleEdgeKeyDown : undefined} → onKeyDown={handleEdgeKeyDown}
tabIndex={isCollapsed ? 0 : undefined}                 → tabIndex={0}

* feat(integrations): show connected credentials on integration detail page

When navigating to /integrations/google-docs (or any integration), a
Connected section now appears above the templates listing all workspace
credentials tied to that provider. Each row links back to the credential
detail page (/integrations/connected/${id}) for management actions.

Pairs with the earlier change that routes connected items from the
integrations list to the provider detail page instead of directly to
the credential detail page.

* fix(integrations): rename Add in chat to Add to Sim

* fix(skills): rename Add button to Add to Sim

* fix(platform): restore M1/M2/M3 regressions and LazyMotion on landing page

M1 — Invitation guard: re-introduce usePermissionConfig().isInvitationsDisabled
alongside the workspace inviteDisabledReason check. The flag now also
respects NEXT_PUBLIC_DISABLE_INVITATIONS and EE permission-group
disableInvitations, not just billing policy.

M2 — Settings redirects: /settings/integrations and /settings/skills
now server-redirect to /integrations and /skills respectively so old
bookmarks and emails don't silently land on General.

M3 — Starter block search exclusion: restore block.type !== 'starter'
guard in the search store so users cannot add a duplicate Starter block
via the command palette.

LazyMotion: restore LazyMotion + domMax/domAnimation wrappers and m.*
components in landing-preview-panel and landing-preview-home. The
removal was accidental (the full motion bundle was left after an
import cleanup), which caused the entire framer-motion feature set to
load eagerly on the landing page.

* fix(integrations): revert connected list to credential detail; remove settings redirects

* feat(sidebar): restore workspace switcher search with updated styling

Shows a search input in the workspace dropdown when the user has more
than 3 workspaces (WORKSPACE_SEARCH_THRESHOLD). Keyboard navigation:
ArrowDown/Up to move through results, Enter to switch, resets on close.

Styled to match the current branch (border-1/surface-5 tokens, sm text,
11px Search icon) rather than the old staging styles. Highlight state
is wired through chipVariants active prop so it follows the same active
appearance as clicked/hovered items.

* fix(sidebar): clean up workspace search — layout, memo, and effect guard

* fix(sidebar): align workspace rename input selection style with workflow rename

* perf(sidebar): eliminate React re-renders during sidebar drag resize

Previously, every mousemove during resize called setSidebarWidth(), which
both updated the --sidebar-width CSS variable (sync) and set Zustand state
(async). This caused:
  1. A 1-frame transition flash on mousedown — isResizing state had to
     round-trip through React before the is-resizing CSS class was applied,
     so the width transition fired for the first pixel of movement.
  2. A React re-render per pixel dragged — components reading sidebarWidth
     from the store (avatars, usage-indicator) lagged one frame behind the
     container, making the + and ... buttons appear to jump ahead.

New approach:
  - handleMouseDown adds is-resizing directly to the sidebar DOM node before
    any React involvement (synchronous, no frame lag).
  - mousemove writes only to the CSS custom property (zero React renders).
  - mouseup persists the final width to Zustand exactly once.
  - isResizing / setIsResizing state removed from the store and hook — they
    are no longer needed since the class is managed via direct DOM mutation.

* perf(sidebar): add requestAnimationFrame throttle to resize mousemove handler

* fix(sidebar): fix drag-right lag caused by WorkspaceChrome overflow-hidden transition

The sidebar-container's is-resizing class correctly suppressed its own width
transition, but the two wrapper divs in WorkspaceChrome both have
transition-[width]/transition-transform with a 175ms ease. The outer wrapper
also has overflow-hidden, so while the sidebar content was at the correct width
instantly, it was visually clipped by the outer wrapper which was still
animating — causing the + and ... buttons to appear to lag behind the resize
line on drag-right (not on drag-left, since shrinking doesn't clip content).

Fix: add sidebar-shell-outer/sidebar-shell-inner class names to both chrome
wrappers, and suppress their transitions via html.sidebar-resizing rule when
a drag is active. The html.sidebar-resizing class is toggled directly in the
resize hook alongside is-resizing, so it takes effect synchronously on mousedown.

* fix(icons): redesign Download icon to match Upload style; fix Upload/Download confusion

Download icon was missing the tray/shelf line at the bottom that Upload has,
making it look like a plain arrow rather than a matched pair. Updated Download
to use the same viewBox, stroke weight, and three-path structure as Upload
(tray + stem + arrowhead), just pointing down.

Also fix 5 places where Upload (↑) was incorrectly used for download/export
actions:
  - files.tsx: two Download action rows in toolbar and context menu
  - tables/table.tsx: Export CSV toolbar button
  - table-context-menu.tsx: Export CSV context menu item
  - logs.tsx: Export toolbar button
  - landing-preview-logs.tsx: decorative Export button

Import CSV and actual upload actions correctly keep the Upload icon.

* fix(icons): replace Upload with Download on all remaining export/download actions

- panel.tsx: Export workflow dropdown item
- context-menu.tsx: Export in sidebar workflow context menu
- chat.tsx: Export chat button
- output-panel.tsx: Export console CSV button
- terminal.tsx: Export console CSV button
- resource-content.tsx: Export table as CSV + Download file buttons

* fix(icons): fix remaining Upload→Download on download actions in files and logs

- action-bar.tsx: download button in files toolbar
- file-row-context-menu.tsx: Download item in file context menu
- file-download.tsx: both download buttons in log details file viewer

* updated block skills, settings pages, modals, buttons -> chips, blocks missing metadata

* updated skill modal

* improvement(resource-header): refine breadcrumb truncation ux

* Fixes

* improvement(resource): add floating overflow text tooltips

* wire up credits counter

* improvement(resource-header): mute path dropdown title

* refactor(resource-header): share floating-tooltip engine, prune dead overlay tooltips (#4844)

Clean up the breadcrumb truncation feature for reuse and correctness:

- Extract useFloatingTooltip / useIsOverflowing / FloatingTooltip into a shared
  floating-tooltip module. BreadcrumbSegment and FloatingOverflowText now consume
  one implementation instead of duplicating ~150 lines of positioning, velocity,
  overflow-detection, and portal logic.
- Replace the hardcoded terminal-label regex in ResourceHeader with a typed
  `terminal` flag on BreadcrumbItem (set by the document chunk/loading crumbs),
  decoupling the generic header from knowledge-base copy.
- Clear the path-popover close timeout on unmount and reuse the shared
  POPOVER_ANIMATION_CLASSES constant.
- Drop the redundant manual overflow-state writes (fixes a sticky fade mask).
- Revert FloatingOverflowText inside Combobox `overlayContent` back to plain
  truncating spans across files/logs/tables/scheduled-tasks/document: the combobox
  overlay is pointer-events-none, so the tooltip handlers never fired there.

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

* refactor(emcn,resource-header): address PR #4844 review feedback

- useIsOverflowing now uses a callback ref so the ResizeObserver follows the
  element across mount/unmount/reassignment instead of capturing it once at mount.
  Safe for conditionally rendered consumers of the shared hook. (greptile P2)
- Move POPOVER_ANIMATION_CLASSES out of chip-date-picker implementation internals
  into emcn/components/popover/popover-animation.ts, exported from the
  @/components/emcn barrel. Consumers now import from the module boundary.
  (greptile P2)

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

* Prompt surface

* Regenerated contracts

* upgrade table and styling upgrade

* Literalness gaps

* Updates

* Remove prefix for running code

* Display names for reads

* Tool catalog

* fix schema to include integration

* fix(files): align delete icon with tables view (Trash → Trash2)

Co-Authored-By: waleed <waleed@simstudio.ai>

* fix(mothership): preserve blockType for integration contexts in sent messages

Integration mention chips were missing their provider icons in sent messages
because blockType was dropped when mapping ChatContext to messageContexts.
renderIntegrationTile returns null without blockType, silently hiding the icon.

* fix(mothership): allow 'integration' resource type in chat resources API

The VALID_RESOURCE_TYPES allowlist was missing 'integration', causing a
400 error when adding integrations to the Mothership resource tab — so
they never persisted and disappeared on refresh.

* fix(ui): Add "File" title next to file resource header

* fix(ui): fix resource header columns being bolded

* fix(resource): keep the floating tooltip from jumping on click

Gate the focus-driven show behind :focus-visible so a mouse click (which
focuses the trigger) no longer re-shows the tooltip anchored to the element's
bottom edge. On click the tooltip now hides cleanly instead of jumping down;
keyboard focus still shows it.

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

* perf(sidebar): eliminate unnecessary re-renders in workspace switcher for non-search users

- onMouseEnter: only set highlightedIndex when showSearch is true, preventing
  a state update + re-render on every workspace row hover for users with ≤ 3
  workspaces where the search is never shown
- onOpenChange: only reset workspaceSearch and highlightedIndex when showSearch
  is true, since both values are always already at their defaults for non-search
  users and setting them triggers a pointless re-render during dropdown close
- data-workspace-row-idx: only set when showSearch is true since the scroll
  effect that reads this attribute is already gated on showSearch

* feat(search): context-aware cmd-k results on the integrations page

When cmd-k is opened on the integrations page, show two new result
groups: connected accounts (visible even with empty input) and catalog
integrations (appear once the user types). Selecting an OAuth integration
deep-links to its detail page with ?connect=oauth so the connect modal
auto-opens. Non-OAuth integrations navigate to the plain detail page.

Both groups are gated to the integrations page only and respect the
hideIntegrationsTab permission. The credentials fetch shares the same
React Query cache key as the integrations page itself (no double fetch).

* refactor(emcn): make the floating tooltip the one canonical Tooltip

Replace the Radix-based emcn Tooltip with the cursor-following floating tooltip so
every tooltip in the app uses one consistent style. Built on the shared
floating-tooltip engine (relocated into emcn), not a parallel implementation.

- Move the floating-tooltip engine into emcn/components/tooltip and export it from
  the barrel; re-point its consumers (FloatingOverflowText, resource-header)
- Extend the FloatingTooltip bubble to render arbitrary children (+ role/id for
  a11y) so it can back general tooltips, not just overflow text
- Rebuild emcn Tooltip (Root/Trigger/Content/Provider/Shortcut/Preview) on
  useFloatingTooltip — compound API preserved, ~350 call sites unchanged, legacy
  side/align props accepted and ignored (the tooltip follows the cursor). Removes
  @radix-ui/react-tooltip usage (package kept for a later cleanup; react-slot
  retained for asChild)

Note: general tooltips now show instantly (no hover delay) and follow the cursor.

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

* style(emcn): put tooltip text on the design scale (text-caption)

Replace the tooltip's ad-hoc `text-xs` + `leading-[18px]` with the semantic
`text-caption` (12px) font-size token so the text styling is fully on the design
scale and self-documenting, matching how the rest of the system is set up. The
color already used the global `--text-body` token. No visual change (still 12px
with a ~18px line height).

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

* Mship byok

* feat(sidebar): add empty task state and inline task creation

- Show "No tasks yet" in the Tasks section (expanded and collapsed) when the list is empty
- Clicking + now creates a task via the API and navigates directly to it, rather than navigating to home
- Add isCreatingTaskRef guard to prevent double-click from spawning multiple tasks
- Disable + button while creation is pending
- Fall back to home navigation on creation error

* invite, billing, home

* Sandbox warning

* improvement(seats): auto purchase seats on invitations into workspace (#4857)

* improvement(seats): auto purchase seats on invitations into workspace

* improve sampling for seat drift reconciler

* address comments

* feat(knowledge): align connector UI with integrations page styling

- ConnectorTypeCard now matches integration rows: brand-colored rounded-xl tile, ArrowRight, title/subtitle hierarchy
- ConnectorCard icon upgraded from flat surface-4 to branded tile (white icon on brand bg, graceful fallback)
- Connector header badges use chipVariants instead of custom Button classes
- Add-connector search input aligned to integrations style (h-[30px], rounded-lg, border-1)

* fix(icons): trim Folder SVG viewBox to remove right-side whitespace

The folder path only extends to x≈14.33 in a 15-unit viewBox, leaving
~0.5 units of empty space on the right. At 12px rendered size this
produces ~0.4px extra gap (visible as ~1px on retina displays) compared
to solid icons like the workflow color square. Trimming the viewBox to
14.5 units makes the folder fill its chip slot evenly.

* fix(user-input): restore draft text synchronously to preserve contexts on nav

The SSR-safe approach (empty useState + effect restore) created a timing
window where the sync effect in useContextManagement fired with message=''
before the value was set, clearing any restored contexts. Folder and workflow
contexts (not re-added by applyAutoMentions) were lost on every nav-back.

Revert to the staging approach: initialize value synchronously from the
draft store so message is already populated when effects run, matching
the behavior on staging.

* fix(queue): render context chips in queued messages

- Remove plainMentions from queued message rows so context chips render
  with icons, consistent with sent messages
- Fix computeMentionRanges to use '/' prefix for skill contexts (content
  has the slash trigger restored at submit time, not '@')

* fix(mothership): remove integrations from add-resource dropdown

* fix(mothership): comment out integrations from add-resource dropdown

* File serializer

* Skills

* chore(db): drop form, templates, template_creators, template_stars tables

These tables backed the Forms and Templates platform features which were
intentionally removed from this branch. Clean up the DB schema to match.

* chore(db): add migration metadata for 0224 drop tables

* block icons, sidebar, toolbar

* chore: remove remaining dead code for template-profile feature

- Remove 'template-profile' from SettingsSection union type
- Remove 'template-profile' entry from SECTION_TITLES
- Remove now-redundant template-profile guard in settings sidebar
- Remove commented-out template-profile nav item

* fix(multi-select): preserve anchor on range selection for tasks and folders

After a shift+click range, the anchor (lastSelectedTaskId / lastSelectedFolderId)
was being updated to the end of the range (toId). This caused subsequent
shift+clicks to extend from the wrong point instead of the original click.

Standard behavior: anchor stays at the initial click (fromId) so repeated
shift+clicks always expand/contract relative to where you started.

* Update skills

* Load skill tool

* Fix vfs dynamic context encoding

* Media subagent

* feat(emcn): add SearchInput component and unify search bars platform-wide

- Add SearchInput to emcn: 30px chip-family filled search input matching the
  integrations page pattern (border-1, surface-5, leading Search icon)
- Migrate all 22 search bars across settings, EE pages, and integrations to
  SearchInput (only layout classes allowed at callsites)
- Rename Sim Keys -> Sim API Keys in nav/title; page copy now says API key
- Remove components/ui input, label, and verified-badge; migrate consumers
  to emcn equivalents or raw inputs (table cell editor, wand prompt bar)
- Delete dead EE skeleton files (data-drains, data-retention)
- General settings: Home Page chip moves to header left as navigation

* fix(files,tables): restore new-file editor autofocus and CSV import error toasts

Both were dropped on the staging line and regressed vs production (main):

- Files: the new-file editor autofocus chain (files.tsx -> file-viewer ->
  text-editor) was stripped by the react-doctor dead-code pass in #4544,
  which misread the prop-drilled `autoFocus` (consumed by an imperative
  `editor.focus()` effect) as unused. Restored the prop through all three
  layers and the one-shot focus effect so creating a new file focuses the
  editor immediately.
- Tables: CSV import failures were silently logged with no user feedback.
  Restored the per-file and generic `toast.error` surfacing.

* Move superagnet back into superagent

* feat(home): score suggested actions by workspace signals

- Derive the suggestion pool from the curated block template catalog
  (1,343 prompts across 172 blocks) instead of 15 hardcoded entries
- Fix inverted relevance: prompts for connected providers are now boosted
  4x (instantly runnable) instead of excluded; unconnected discounted 0.4x
- Weight by featured (3x), popular category (1.5x), and resource gaps
  (no tables -> boost table starters; has KBs -> dampen KB-creation prompts)
- Weighted sampling without replacement, max one suggestion per block
- Connect rows weighted by catalog template count; 2 for fresh workspaces,
  1 once something is connected
- Key the catalog map by both versioned and base block types so gmail_v2
  templates resolve (gmail, github, notion, linear were silently dropped)
- Replace derive-in-effect state with a useMemo keyed by a shuffle nonce
- Add suggested_action_clicked / suggested_actions_shuffled /
  suggested_actions_toggled PostHog events

* billing, teammates

* improvement(credentials): credentials invites, secrets tab wiring up (#4874)

* improvement(credentials): move away from invite notion

* wire up secrets ui/ux

* address comments

* get consistent styling by removing emcninput + text area

* styling consistency

* remove fallback

* address comment:

* refactor(ui): migrate settings & workspace UI to chip design system

Migrate modals to ChipModal (showDivider, hint, resizable, size, leading,
ChipModalTabs), standardize Chip variants, add ChipCombobox wrapper, and apply
chip inputs/dropdowns across settings, knowledge, logs, tables, inbox, EE tabs.
Render ChipModalTabs as a ChipSwitch segmented control. Align /settings/secrets
detail with /integrations, refresh whitelabeling, and restore file-editor
autofocus and CSV import error toasts.

* Update media

* fix(mothership): restore integrations to useAvailableResources for @ mention

Integrations were fully removed from useAvailableResources which broke
the @ mention menu since user-input shares the same hook. Now integrations
are always included in the hook but excluded at the AddResourceDropdown
component level, keeping them out of the sidebar + menu while remaining
available for @ mention autocomplete.

* refactor(settings): chip design-system consistency pass across all tabs

Extract a shared chip-field shell (CHIP_FIELD_SHELL/CHIP_FIELD_INPUT) mirroring
Input variant='chip' and route secrets, credential detail, and integrations
credential detail through it (30px height, font-medium, focus ring). Add a
Discard action to the secrets header when dirty. Group BYOK providers into
Models/Search & web/Enrichment sections and align its tiles to the integrations
tile.

Normalize list-row typography to text-[14px]/text-[12px] and icon tiles to
rounded-xl + border across api-keys, copilot, custom-tools, mcp,
workflow-mcp-servers, credential-sets, and access-control. Tone the secrets
Details chip and per-row affordances to ghost. Fix token correctness: raw
tailwind colors to design tokens (data-drains), missing chip variant on the
Snowflake role input, ColorInput chip-field reuse (whitelabeling), error token
and icon sizes (workflow-mcp-servers), border token (mcp), no-results sizing
(secrets), row chrome (recently-deleted), hover token and Button to Chip
(access-control), and deduped textarea chrome (sso).

* feat(settings): unify filter dropdowns on ChipSelect (integrations style)

Add a ChipSelect emcn component — a filled chip trigger + chevron opening a
DropdownMenu, matching the integrations category filter — supporting single
select, multi-select (checkbox rows), grouped options, and optional in-menu
search. Migrate every settings/EE filter dropdown off ChipCombobox to it:
audit-logs (resource-type multi + time-range), data-retention, data-drains,
general, admin (grouped tool picker), inbox status filter, and the workflow
MCP-server pickers. The SSO provider-id field stays an editable combobox since
it accepts free-text slugs.

Also fix audit findings: chip the MCP client-secret input, normalize an MCP
error-text size, and drop now-dead destination-icon code in data-drains.

* fix(mentions): require explicit @ for integration mentions; decorate sent messages robustly

- Bare integration names in prose (Monday, Notion, Clay) are no longer
  auto-converted to mentions or chipped — mention treatment is strictly
  opt-in via a token-starting @ (fixes the scunthorpe problem)
- @-prefixed mentions still canonicalize casing (@slack -> @Slack) on both
  the keystroke fast-path and bulk paths (paste, template, draft, STT)
- Sent/queued messages now self-sufficiently decorate @IntegrationName
  tokens via a text scan, covering messages sent before the input pass
  ran or authored outside the chat input
- Integration contexts missing a resolvable blockType (messages persisted
  before blockType was saved) are backfilled by label lookup so their
  mention pills render the brand icon again

* refactor(settings): section the API keys page like secrets

Wrap Workspace, Personal, and the allow-personal-keys toggle in SettingsSection
(muted label + divider) instead of bare bold headers, matching the secrets and
BYOK pages.

* fix(settings): ChipSelect renders above modals + full-width form mode

Raise the ChipSelect menu to --z-popover so it layers above modal surfaces
(--z-modal) instead of opening behind them. Add a fullWidth prop that stretches
the trigger and right-aligns the chevron for form-field use, and apply it to the
workflow MCP-server pickers.

* fix(emcn): ChipSelect uses the emcn flat chevron, not lucide's square one

The lucide ChevronDown is square; rendering it at the chip's 9x7 footprint
stretched it. Switch to the custom emcn ChevronDown (built for that wide aspect),
matching the integrations filter and ChipDropdown.

* fix(emcn): ChipSelect trigger hugs its content (w-fit)

In a stacked form layout the trigger was stretched by align-items: stretch,
leaving an empty gap to the right of the value. Add w-fit so the chip sizes to
its content (a compact pill) everywhere; fullWidth form selects are unaffected.

* fix(emcn): ChipSelect uses a square lucide chevron

Revert to lucide's ChevronDown sized square (size-[14px]) so it renders crisp,
matching the standard select chevron used by Combobox.

* renamed tasks to chats

* rename and file change

* Wspace resource refs

* improvement(billing): wire up billing, org, teammates tabs + remove deprecated subscription tab (#4887)

* improvement(billing): wire up billing, org, teammates tabs + remove depr subscription tab

* pass exec timeout to tool routes

* reuse helper

* address comments

* address disable comment

* Vfs updates

* chore(db): remove migration 0224 to regenerate on top of staging

Co-authored-by: Cursor <cursoragent@cursor.com>

* VFS updates and linter

* fix type errors and regen migration?

* File block v5

* Remove docs

* Fix

* Doccer image

* Nuke the dag

* chore(db): drop branch migration 0226 ahead of staging merge; will regenerate

* chore(db): regenerate migration 0226 after staging merge

* Fix lint

* externalize before compaction in fallback'

* Run workflow improvements

* Loosen run options validation

* Lint

* fix save/discard chips to be consistent

* fix(ui): remove smodal tabs in favor of chip modal tabs

* update byok manager component

* fix(ui): skip auto-scrolling on mouse highlight of workspace

* Add creds to wspace context

* Wscontext

* fix(platform): restore settings redirects, forgot-password Enter submit, and tag tooltip visibility

- Re-add SETTINGS_REDIRECTS so /settings/integrations and /settings/skills
  deep links redirect to their top-level routes instead of rendering an
  empty settings panel (accidentally removed in 86da193cc3 one minute
  after cca5054cf6 added it)
- Add opt-in onSubmit to ChipModalField input/email variants and wire it
  in the forgot-password modal so Enter submits again (lost in the
  ChipModal conversion)
- Knowledge tag tooltip: drop the max-h/overflow-y-auto clamp that the
  pointer-events-none floating tooltip made unreachable; truncate each
  tag row instead so all tags stay visible with bounded height

* feat(usage-limits): org member specific limits (#4893)

* feat(usage-limits): org member specific limits

* revert feature flags

* fix type issues

* address comments

* address comments and address other billing gapes'

* fix

* improvements

* fix remaining credits display

* fix

* pass ws id to route validations

* chore(db): remove migration 0226_third_spot before staging merge

* chore(db): regenerate migration as 0227 after staging merge

* Block

* feat(telemetry): add posthog + audit coverage for new platform actions

Audit log (compliance/permission-relevant only):
- org_seat.provisioned — seat auto-purchased when an invite acceptance
  grows the org (actor = accepting user, includes seat delta)
- org_plan.converted — Pro→Team conversion triggered by invite acceptance
- org_seat.drift_reconciled — hourly cron healed a drifted seat count
- credential_member.added/removed/role_changed — credential sharing
  surface was previously fully unaudited
- table.created — parity with existing table.updated/deleted
- skill updates now record skill.updated instead of mislabeled
  skill.created

PostHog:
- seats_provisioned, credential_shared/unshared,
  environment_updated/deleted (key counts only, never names/values)
- table_import_started/completed — background CSV imports previously had
  zero failure observability
- table_exported, file_downloaded, skill_updated
- credential_connected now fires for OAuth completions (draft-hooks),
  credential_deleted for OAuth disconnects; previously only manual
  credentials were tracked
- table_workflow_run gains deployment_mode (live/deployed/mixed)

Also: logger.warn on all new credential-admin 403 denials (members +
environment routes); invite-created audit enriched with
enforcedFixedSeats/plan.

Deliberately excluded as noise: per-hour dead-letter audit rows (would
re-record the same stuck event every cron run) and a duplicate
system-actor org_plan.converted in the Stripe outbox handler.

* feat(home): fill textarea on suggested prompt click instead of sending

Clicking a prompt action in the Suggested Actions panel now populates
the Mothership user-input textarea (via applyAutoMentions) and focuses
it with the caret at end, rather than immediately submitting. The user
can review, edit, and send manually.

* fix(templates): name owning integration in featured block template prompts

Four featured prompts omitted their owning integration name, making them
unbranded and disconnected from their title. Each prompt now explicitly
names GitHub or Google Sheets so mentionifyIntegrations renders the chip
and the copy reads as a self-contained agent-building instruction.

* fix(templates): rewrite fragment prompts so each names its integration and reads as a complete instruction

Featured and non-featured block template prompts that either omitted
the owning integration's canonical name or were phrased as marketing
fragments rather than natural user instructions have been rewritten.
Each updated prompt now starts with an imperative verb ("Build a
workflow that…"), names the owning integration explicitly so the
@-mention chip renders correctly, and aligns with the entry's title.

Files changed: salesforce, hubspot (×2), github, slack, airtable,
firecrawl, iam.

* fix(templates): name integration in remaining block template prompts

- google_docs.ts: replace 'Google Doc' with 'Google Docs document' in 4 prompts; update title 'Meeting notes to Google Doc' to 'Meeting notes to Google Docs'
- google_sheets.ts: replace 'Google Sheet' with 'Google Sheets spreadsheet/Google Sheets' in 3 non-featured prompts
- slack.ts: replace 'Google Doc' with 'Google Docs document' in 'Daily standup summary'
- stripe.ts: replace 'Google Sheet'/'Slacks' with 'Google Sheets'/'Slack' in 'Weekly metrics report'
- reddit.ts: add 'Reddit' to 3 prompts that only referenced subreddits
- notion.ts: rewrite featured prompt to start with a verb and name Notion
- jira.ts: rewrite featured marketing-fragment prompt to start with a verb
- linear.ts: rewrite featured marketing-fragment prompt to start with a verb
- gmail.ts: rewrite featured marketing-fragment prompt to start with a verb and name Gmail

* fix(icons): convert monochrome dark brand icons to currentColor for dark mode

LinkupIcon, InfisicalIcon, IntercomIcon, LumaIcon, GranolaIcon, OnePasswordIcon,
and RailwayIcon were hardcoded to black/near-black fills/strokes, making them
invisible in bare DARK mode. Convert all to currentColor so they follow the
theme-aware text color.

Add iconColor: '#286efa' to IntercomBlock (Intercom brand blue is a confident
mid-tone, safe on both themes).

Two-tone icons (StagehandIcon, AgentPhoneIcon, QuiverIcon) are left unchanged
because their white details are structural — converting to single-tone would
destroy logo legibility.

* removed color, user input, sidebar, suggested actions, chips/emcn

* removed color migration

* Table bump

* improve(blocks): audit block catalog metadata for accuracy and fill gaps

- Fix template prompts that claimed capabilities blocks don't have
  (fabricated triggers, non-existent tools) across ~98 integrations
- Normalize integration tags to family conventions and valid union values
- Align alsoIntegrations and modules with template prompt content
- Add BlockMeta for circleback, imap, and rss trigger integrations
- Add templates to clickhouse and greptile metas
- Remove duplicate PageSpeed deploy-gate template

* chore(telemetry): drop org_seat.drift_reconciled audit

System self-heal bookkeeping doesn't belong in the user-facing audit
trail — membership and seat-purchase changes are already audited, and
the cron's logger output covers ops visibility.

* chore(db): consolidate branch migrations into single 0227

* fix(emcn): make ChipModal scroll internally when content exceeds viewport

The Modal→ChipModal migration dropped the old ModalBody scroll container:
ChipModal renders ModalContent bare (no overflow-hidden) and its wrappers
had no min-h-0 chain, so tall modals (e.g. New data drain with an S3
destination) overflowed max-h-[84vh] off-screen with no way to scroll.

Complete the flex min-h-0 chain through ChipModal's frame and give
ChipModalBody flex-1 min-h-0 overflow-y-auto — header/footer stay pinned,
body scrolls only when constrained. Short modals are unaffected (max-h
caps, it doesn't stretch), and dropdowns inside the body are Radix-portaled
so the new scroll container cannot clip them. Five modals that had locally
patched this with max-h-[Nvh] overrides keep working unchanged.

* fix(emcn): don't close modal when dismissing a dropdown via outside click

Radix dispatches pointer-down-outside to every open dismissable layer at
once, so clicking outside an open dropdown/select inside a modal closed
both the dropdown and the modal in one jarring step. ModalContent now
prevents its own dismissal while a portaled popper layer is open — the
first outside click closes just the popper, the next one closes the
modal.

* docs(skills): de-duplicate and correct agent docs; canonical styling tokens; mirror sim-sandbox rule

* docs(skills): broaden boundary-raw-fetch scope note, sync cursor rule mirrors

* fix(emcn): harden modal popper guard and exempt caret-anchored dropdowns from body scroll

Adversarial review follow-ups to the ChipModal scroll + dismissal fixes:

- Popper guard now requires data-state="open" inside the popper wrapper,
  so a dropdown that is merely animating closed no longer swallows the
  next outside click on the modal (DropdownMenu has an exit animation
  that keeps its wrapper mounted briefly)
- Port the same guard to SModalContent for consistency
- custom-tool-modal: opt its body out of the chrome scroll container
  (flex-none + overflow-visible); the caret-anchored EnvVar/Tag
  autocomplete dropdowns are absolute-positioned inside the body and
  must spill past its bounds rather than clip against a scroll boundary

* refactor(billing): drop unnecessary useCallback wrappers from event handlers

* fix(data-drains): complete chip migration of destination forms and fill missing placeholders

Finishes the in-flight FormField → ChipModalField conversion for all
destination form specs and adds the placeholders that several inputs
never had (S3 bucket/region/access keys, Azure account key, Datadog API
key, webhook signing secret/bearer token) — the cause of the New Data
Drain modal showing placeholder-less inputs inconsistently.

* fix(emcn): broaden modal popper guard to onInteractOutside

Covers the focusOutside dismissal path too: when a popper's focus scope
unwinds on close, the transient focus shift could still dismiss the
modal (and simultaneous body pointer-events lock teardown could freeze
the page). Same data-state="open" scoping as the pointer guard.

* fix

* fix(emcn): harden modal outside interactions

* Remove migration 0227 to regenerate after merge

Drop the 0227_org_member_usage_limit migration, its snapshot, and journal
entry so it can be regenerated cleanly after merging improvement/platform.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Regenerate migration as 0228 after merging improvement/platform

drizzle-kit generate re-emits the org_member_usage_limit table, document
uploaded_by, and table_run_dispatches triggered_by_user_id changes on top of
improvement/platform's 0227 snapshot. Also collapse the admin.tsx emcn import
left multi-line after merge conflict resolution.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix pre-existing type errors surfaced by post-merge type-check

These were present on dev before the merge; the post-merge type-check made
them visible.

- workspace-vfs.ts: materializeEnvironment now emits full oauth integration
  objects (id, providerId, displayName, role) so the summary matches
  WorkspaceMdData['oauthIntegrations'] (consumer reads credentialId from id)
- workflows/utils.ts: narrow resolved workflow rows to non-null workspaceId
  (the inArray filter already guarantees it) so the resolved result type holds

Co-authored-by: Cursor <cursoragent@cursor.com>

* Slack trigger

* fix tool mark complete bugs

* change thinking icon to just be blimp

* fix tests

* MCP fixes

* Fix files

* refactor use chat

* multiple fixes

* Fix deploys

* fix billing, tracing, retry lifecycle issues

* remove stale generated migration

Drop migration 0228 and its metadata so it can be regenerated after merging staging.

Co-authored-by: Cursor <cursoragent@cursor.com>

* merge staging in

* fix sockets err classification

* sockets invite flow fix

* remove stale generated migration 0229 before staging merge

Co-authored-by: Cursor <cursoragent@cursor.com>

* regen migration

* Update bun lock

* Fix schema issues

* Fix lint

* Fix build

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: andres <k62hc5kjst@privaterelay.appleid.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com>
Co-authored-by: waleed <waleed@simstudio.ai>
2026-06-09 13:39:51 -07:00
WaleedandTheodore Li 540835a7a5 feat(integrations): add AWS AppConfig integration with tools, block, and docs (#4928)
* feat(integrations): add AWS AppConfig integration with tools, block, and docs

* fix(integrations): preserve latestVersionNumber 0 and tighten locationUri type in AppConfig

* fix(integrations): use valid IntegrationTag values in AppConfig BlockMeta

* feat(appconfig): add list_hosted_configuration_versions operation

* fix(appconfig): stringify configurationVersion in block params

* feat(appconfig): add full CRUD for applications, environments, and configuration profiles

Adds get/update/delete for applications, environments, and configuration
profiles, plus delete_hosted_configuration_version — 10 tools rounding out
the integration to management-grade CRUD completeness.

---------

Co-authored-by: Theodore Li <theo@sim.ai>
2026-06-09 13:17:40 -07:00
Waleed 24f04162fb feat(integrations): expand tool coverage, audit integrations, regen docs (#4920)
* feat(integrations): expand tool coverage, audit integrations, regen docs

Integrations
- Audit and expand Railway, Reddit, Vercel, Sentry, Granola, LaunchDarkly,
  Infisical, Intercom, Luma, 1Password, NeverBounce, and ZeroBounce tools
  against their live API docs; correct request/response shapes and outputs
- Add new tools across Railway (services, deployment lifecycle, variables),
  Reddit (mod actions, user/subreddit reads, messaging), Vercel (domains,
  webhooks, edge config, deployment promotion), Sentry (teams), and Granola
  (folders); register all in the tool registry
- Infisical: fix delete to send params in the JSON body and complete the
  secret output fields (actor, metadata isEncrypted, rotation/reminder fields)

UI
- Update the user-input tooltip copy
- Adjust the feedback modal textarea sizing

Docs
- Regenerate tool/trigger docs to match current source

* rm comments from registry

* feat(integrations): expand Kalshi, Luma, Rootly, Polymarket, Railway tools; regen docs

- Add and refine tools across Kalshi, Luma, Rootly, Polymarket, Railway,
  LaunchDarkly, and Sentry; register all new tools
- Regenerate tool docs to match the updated source

* chore(integrations): update Kalshi get_balance, drop polymarket test, regen docs

* chore(integrations): update Kalshi get_events and subblock migrations, regen docs
2026-06-09 10:52:43 -07:00
Waleed a72e35e2f4 feat(sendblue): add Sendblue iMessage/SMS integration with tools and triggers (#4917)
* feat(sendblue): add Sendblue iMessage/SMS integration with tools and triggers

* fix(sendblue): address review — status-aware webhook dedup, shared routing map, uniform output casing, api-key authType

* fix(docs-gen): skip nested array `items` descriptor in tool input tables

* chore(sendblue): use SendblueSendStyle type, trim URL identifiers

* fix(sendblue): keep is_outbound routing map local to the webhook handler

Avoids a webhook-providers -> triggers cross-subgraph import (single source
of truth in the handler, its only runtime consumer).

* fix(sendblue): remove invalid `tags` from block config (belongs on BlockMeta)
2026-06-08 19:41:09 -07:00
25771358d0 feat(enrichment): add ZeroBounce, NeverBounce, and MillionVerifier email verification (#4854)
* feat(enrichment): add ZeroBounce, NeverBounce, and MillionVerifier email verification

* docs gen

* mdx

* fix(zerobounce): handle 200-status API errors and guard JSON parsing

* fix(byok): render new email-verify providers + raise hosted rate limits

- Add zerobounce/neverbounce/millionverifier to the Enrichment
  PROVIDER_SECTIONS so they render in the workspace BYOK settings UI
  (they were in PROVIDERS + the API allowlist but no section listed them).
- Raise ZeroBounce and MillionVerifier hosted per-workspace rate limits
  from 60 to 1200 req/min, sized against documented upstream ceilings
  (ZeroBounce 80k/10s; MillionVerifier 160/sec). NeverBounce stays at 60
  pending its account-configured throttle.

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

---------

Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 17:45:18 -04:00
0075ab9cf6 improvement(platform): remove tour, simplify sidebar/header, drop loading skeletons (#4354)
* improvement(platform): workspace UI/UX overhaul + integrations catalog

Rework the workspace around the AI-workspace model: a Mothership home, a
top-level Skills route, connected-credential and integration-detail pages,
and a polished sidebar/settings surface. Replace the notifications store
with a unified toast system (provider-level dismiss/pause, countdown ring).

Integrations & catalog:
- Add a BlockMeta layer (tags + catalog templates) scoped to catalog-visible
  integrations; every catalog integration carries >=7 grounded templates.
- Rework the taxonomy: each block declares category tools|blocks|triggers.
  3rd-party services are 'tools'; first-party primitives (postgres, mysql,
  knowledge, file, search, stt/tts, image/video generators, thinking, etc.)
  are 'blocks'. Versioned blocks follow the upgrade paradigm (old hidden,
  latest in toolbar/docs).
- Generate integrations.json + tool docs canonically from block configs.

Architecture & cleanup:
- Consolidate block data extraction behind a single latest-version strategy
  (getCanonicalBlocksByCategory; version-consistent getBlockMeta).
- Unify version-suffix handling in @sim/utils/string (stripVersionSuffix /
  isVersionedType, with tests); registry, generate-docs, tools/utils, and
  integrations all route through it.
- Repair latent broken barrels, remove dead code, fix BlockMeta-related type
  errors and 5 broken docs links.

Behavior-preserving for block execution and the toolbar's tool/block listing.

* refactor(platform): remove forms, templates, and creators features

Remove three standalone features and their supporting code:
- Forms: form-deployment pages, API routes, execution path, and docs.
- Templates: the template gallery (landing + workspace) and template APIs.
- Creators: creator-profile routes and contracts.

Add a super-user permissions module (lib/permissions/super-user) and an
organizations API contract; update the audit/db/testing packages, billing,
and the session/theme providers accordingly.

* test(workflows): update archiveWorkflow update count after forms removal

The forms feature was removed, dropping the form-table update from
archiveWorkflow. Update the stale assertion from 8 to 7 tx.update calls.

* upgrade

* improvement(knowledge): polish tag filter dropdowns (#4816)

* improvement(logs): object storage backed tracespans (#4787)

* improvement(logs): obj storage backed tracespans

* fix storage write context

* fix tests

* address comments

* address comments

* chore(db): remove migration 0219 to regenerate after staging merge

Drops the 0219_robust_shard SQL, its snapshot, and the journal entry so the
trace-spans/cost schema migration can be regenerated on top of the latest
staging migration chain (avoids a number collision with staging's migrations).

Co-authored-by: Cursor <cursoragent@cursor.com>

* improvement(billing): accurate per-member usage via shared ledger helper

Per-member/per-user usage in the org-member routes now adds the usage_log
ledger to the currentPeriodCost baseline (which is no longer incremented),
via a shared getOrgMemberLedgerByUser helper to avoid repeating the
subscription→period→ledger lookup across the admin and member-facing routes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* regen migrations

* update migration

* address comments

* more code cleanup

* incorrect type cast

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* improvement(providers): harden OpenAI-compatible providers + add tests (#4796)

* improvement(providers): harden OpenAI-compatible providers + add tests

* fix(vllm): let tool-loop errors propagate instead of returning silent partial success

* fix(litellm): force tool_choice 'none' on final structured-output call

The deferred final call used tool_choice 'auto', so the model could emit
another tool_calls round instead of the structured answer, leaving content
stale. Use 'none' (matching vLLM/Fireworks) on both the streaming and
non-streaming final calls so the model must return the structured response.

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

* fix(providers/ollama): drop tools from post-tool streaming call

Ollama ignores tool_choice (not in its supported fields), so vLLM/Fireworks'
tool_choice:'none' guard is a no-op here. Omit tools from the final streaming
payload instead so the summarization turn can't emit dropped tool calls.

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

* fix(litellm): spread payload into deferred final call so reasoning_effort carries over

The non-streaming deferred finalPayload hand-picked fields and dropped
reasoning_effort (and any future payload field), diverging from the streaming
path which spreads ...payload. Spread payload here too for consistency.

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

* chore(providers/ollama): restore enrichment TSDoc block

Keeps parity with sibling Chat Completions providers (cerebras/mistral/xai).

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

* docs(fireworks): restore TSDoc on utils helpers

Restore the TSDoc blocks on supportsNativeStructuredOutputs,
createReadableStreamFromOpenAIStream, and checkForForcedToolUsage —
TSDoc is the codebase documentation standard and should not have been
stripped.

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

* chore(litellm): remove inline rationale comments (codebase uses TSDoc)

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

* chore(providers/ollama): drop orphaned enrichment TSDoc

The block documented a function that now lives in trace-enrichment.ts, so it
documents nothing in this file.

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

---------

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

* chore(copilot): deprecate mcp server (#4797)

* chore(copilot): deprecate mcp

* update error codes

* deprecate copilot api v1 route

* feat(integrations): hosted API keys for Findymail, Prospeo, and Wiza (#4777)

* feat(integrations): hosted API keys for Findymail, Prospeo, and Wiza

Add hosted-key support across all credit-consuming Findymail, Prospeo, and Wiza operations so Sim provides the key when a workspace has not brought its own. Register the three BYOK providers, consolidate Wiza's two-step reveal into a single polling wiza_individual_reveal op, and hide the API key field on hosted Sim for hosted operations.

* fix(integrations): harden Wiza reveal polling, soften enrichment getCost guards

Address Greptile + Cursor Bugbot review on #4777: return explicit failures from the Wiza individual_reveal poller instead of throwing (thrown errors were swallowed into a false queued success), short-circuit when the initial reveal is already terminal, tolerate transient 5xx/429 during polling, and return 0 (not throw) from Findymail getCost when the contacts/employees array is absent.

* chore(integrations): biome formatting after wiza merge resolution

* fix(wiza): type isTerminalReveal param structurally for next build typecheck

* feat(enrichments): add Findymail, Prospeo, Wiza to work-email waterfall

* feat(enrichments): add Wiza + Prospeo phone reveal to phone-number waterfall

* feat(enrichments): opportunistic identifiers + LinkedIn URL input across work-email & phone cascades

* fix(tables): reduce column header chevron size and fix sidebar shadow bleed (#4800)

* feat(slack): add install + privacy section to integration landing page (#4799)

* feat(slack): add install + privacy section to integration landing page

Adds a hand-authored, slug-keyed landing-content module (separate from the generated integrations.json so it survives regeneration) and renders an install walkthrough + privacy-policy link on integration pages when present. Also refreshes generated docs (data-enrichment entry, icon mappings, tool mdx).

* fix(landing): render privacy section independently, align CTA analytics label

* docs(landing): clarify the Slack install button is behind sign-in

* refactor(landing): bake integration landing content into generated json via docs-gen

Moves landing content (install walkthrough + privacy) out of a render-time augment and into the generation pipeline: generate-docs reads the pure-data content map and writes landingContent into integrations.json, so the page reads a single source (integration.landingContent). Canonical types live in integrations/data/types.ts.

* improvement(enrichments): align enrichments sidebar with design system (#4801)

* improvement(enrichments): align enrichments sidebar with design system

* fix(enrichments): consistent close button pattern and fix url link hover

* fix(misc): upgrade path change for new better-auth version, billing issue for workflow block agent usage (#4803)

* fix(misc): upgrade path change for new better-auth version, double-billing for workflow block agent usage

* fail loudly if stripe sub id missing

* fix(copilot): seq migration (#4804)

* chore(db): drop redundant idx_webhook_on_workflow_id_block_id index (#4809)

Removed because (workflow_id, block_id) is a left-prefix of idx_webhook_on_workflow_id_block_id_updated_at_desc, which fully covers it. The dropped index was non-unique and enforced no constraint.

* perf(copilot): read chat transcripts from copilot_messages (R+1 cutover) (#4808)

* perf(copilot): read chat transcripts from copilot_messages, not JSONB

Flip user-facing chat reads from the legacy copilot_chats.messages JSONB
array (5.7GB, 99% TOAST) to the normalized copilot_messages table via a
new loadCopilotChatMessages helper ordered by seq NULLS LAST, created_at,
id — the verified canonical order. Both chat-detail getters
(getAccessibleCopilotChat, getAccessibleCopilotChatWithMessages) now drop
the messages column from their metadata select (no more whole-array
detoast on every load) and assemble the transcript from the table after
authorization. This cascades to the copilot + mothership GET endpoints
and to resolveOrCreateChat's conversationHistory (the LLM payload).

The normalize/effective-transcript pipeline is source-agnostic
(copilot_messages.content == a JSONB array element), so transcripts are
byte-identical. Dual-write and the JSONB column stay in place as the
internal-logic source and fallback; removing JSONB writes is a later step.

Prod integrity verified before cutover: 0 messages missing, 0 NULL-seq,
0 dup keys/seq, 0 orphans, order-parity vs JSONB = 0 mismatches.

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

* test(copilot): cover auth-deny on a found row skips the messages query

Address PR review: exercise the `if (!authorized) return null` contract —
when the chat row exists but authorization fails, the getter returns null
and never issues the copilot_messages read.

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

---------

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

* fix(tables): right-align run/stop in embedded toolbar; workflow cells format like normal cells (#4806)

* fix(tables): right-align run/stop in the embedded table toolbar

Add a right-aligned `trailing` slot to ResourceOptionsBar and move the embedded
mothership table's run/stop control into it, so Filter + Sort stay left-aligned
and run/stop sits opposite on the right. No-op for the search-bearing consumers
(logs, resource list), which don't pass `trailing`.

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

* fix(tables): workflow-output cells format values like normal cells

Workflow-output columns short-circuited in resolveCellRender and rendered their
value as plain text, so a sim-resource URL / external URL / JSON / date produced
by a workflow never got the chip, favicon link, or typed formatting a normal
cell gets. Factor value formatting into a shared `resolveValueKind` helper used
by both the workflow-value branch and the plain-cell branch; the workflow branch
keeps the typewriter reveal for plain streaming text via a `typewriter` flag.

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

* fix(tables): detect resource/URL links on workflow output regardless of column type

Workflow output columns default to `json` (columnTypeForLeaf), so routing their
values through the type-based formatter (a) gated chip/URL promotion behind
`column.type === 'string'` — a URL produced by a json-typed output never became
a chip — and (b) JSON.stringify'd plain string values, adding quotes and losing
the typewriter reveal. Detect links (sim-resource chip / favicon URL) on the
value string directly for workflow outputs, falling back to the plain `value`
kind; plain cells keep the type-based formatting. Addresses Greptile P2 on #4806.

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

---------

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

* fix(icons): repair broken integration icon rendering (#4810)

* fix(icons): repair broken integration icon rendering

Two distinct bugs left integration icons broken on the /integrations page
(visible at 32-40px, hidden at the toolbar's 16px):

1. Corrupted SVG paths (Notion, Greptile, Granola, Calendly, Grafana, Bedrock):
   over-minified data dropped elliptical-arc flag digits (e.g. `A1 1 0 5.9 7`
   instead of `A1 1 0 0 0 5.9 7`); Granola's cubic stream was truncated. Browsers
   abort path parsing at the first invalid arc flag, so each rendered as a fragment
   or blank. Replaced with correct path data from canonical sources, preserving each
   icon's existing fill/gradient and bgColor.

2. Invisible glyph (Bright Data): its icon uses fill='currentColor' but bgColor was
   '#FFFFFF', and every surface forces text-white on the glyph - white-on-white.
   Changed bgColor to Bright Data's brand blue (#3d7ffc) so the white glyph reads,
   matching the white-glyph-on-brand-chip convention.

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

* fix(icons): restore Calendly dual-tone brand colors

Addresses review feedback: the previous fix replaced the broken Calendly icon
with a monochrome #006BFF path, dropping the cyan #0ae8f0 accent from the
original dual-tone mark. Restored the two-tone logo (blue + cyan) using clean,
valid path data, cropped to a tight square viewBox so it fills the chip.

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

* improvement(icons): enlarge icons, fix Zoom contrast and Quiver chip

- Zoom: glyph was blue-on-blue (#0B5CFF on #2D8CFF chip); switched to
  currentColor so it renders as a white glyph on the blue chip.
- Quiver: chip bgColor #000000 -> #FFFFFF to match the icon's near-white box,
  and enlarged the mark slightly (viewBox crop).
- Enlarged (tightened viewBox, verified no clipping): RevenueCat, Prospeo,
  Granola, Firecrawl, Enrich.so, and the AWS icons (RDS, DynamoDB, SQS,
  CloudFormation, Athena, CloudWatch, SES, Bedrock, S3).
- ZoomInfo left unchanged: it is a full red rounded-square logo that already
  fills its frame, so a crop would clip it.

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

* fix(icons): use Bright Data wordmark on white chip; repair Circleback

- Bright Data: replaced the flame glyph with the official two-tone 'bright data'
  wordmark (provided asset), centered in a symmetric viewBox. Reverted the chip
  bgColor from #3d7ffc to #FFFFFF since the blue wordmark is invisible on a blue
  chip (the wordmark is designed for a light background).
- Circleback: a minifier had rounded the pattern's image scale to scale(0),
  collapsing the embedded logo to zero size (invisible). Restored the correct
  scale (1/280 = 0.00357142857) so the C. mark renders.

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

* fix(docs): sync Quiver block color card to white chip

Reflects the Quiver bgColor change (#000000 -> #FFFFFF) in the docs block info card.

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

* improvement(icons): enlarge AWS/Cloudflare/Dagster icons, fully white Zoom

- Enlarged (tighter viewBox, render-verified, no clipping): Cloudflare, Dagster,
  and the red AWS icons AWS IAM, Identity Center, Secrets Manager, SES, STS.
  Identity Center was anomalously small (filled ~32% of its frame); the group is
  now sized consistently (~80% fill).
- Zoom: the camera lens triangle was still #0B5CFF (blue-on-blue); switched it to
  currentColor so the whole camera renders white on the blue chip.

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

* docs(wiza): consolidate individual reveal into a single operation

Merges the separate Start/Get Individual Reveal operations into one Individual
Reveal operation in the Wiza docs and integrations data (operationCount 5 -> 4).

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

* improvement(icons): size remaining AWS icons to match the set (~80% fill)

Bring RDS, DynamoDB, SQS, CloudFormation, Athena, CloudWatch and S3 up to the
same ~80% fill as the AWS IAM/Identity Center/Secrets Manager/SES/STS group, so
all AWS icons are visually consistent. Bedrock left as-is (already ~92% fill).

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

* fix(icons): use Bright Data flame mark, enlarge ZoomInfo

- Bright Data: the full 'bright data' wordmark was illegible at chip size.
  Replaced with just the flame-'i' brand mark (blue #4280f6 on the white chip),
  centered.
- ZoomInfo: cropped the viewBox toward the white 'Zi' so it's larger; the red
  rounded-square background still fills the chip.

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

* improvement(icons): enlarge CrowdStrike icon

The falcon mark sat small in its chip because the icon used a wide 768x500
viewBox (letterboxed in the square chip). Switched to a square viewBox centered
on the mark so it fills ~80%, consistent with the other icons.

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

---------

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

* fix(tables): serialize schema mutations to prevent parallel column clobber (#4812)

* Make workflow description nullable

* fix(tables): serialize schema mutations to prevent parallel column clobber

* fix(tables): load workflow outside schema lock; use DbOrTx for getTableById

* fix(tables): scale idle timeout in updateColumnType to avoid aborting large type changes

* fix(tables): skip stale remap types when workflowId changes concurrently

* fix(tables): scale idle timeout in updateColumnConstraints for large tables

* fix(wait): resume live/draft async waits and preserve cell context on chained waits (#4814)

* Make workflow description nullable

* fix(wait): resume live/draft async waits and preserve cell context on chained waits

* improvement(knowledge): polish tag filter dropdowns

* improvement(knowledge): soften filter section labels

* improvement(knowledge): soften list filter labels

* fix(security): harden SSO domain registration, webhook path isolation, and CSV export (#4813)

* fix(security): harden KB file access, SSO domain registration, webhook path isolation, env secrets, and CSV export

* fix(sso): scope domain conflict query with indexed lower(domain) filter

Address PR review: avoid a full-table scan on every SSO provider
registration by filtering candidate rows in SQL with
lower(domain) = <normalized>, keeping the in-memory ownership check.
Also tighten the normalizeSSODomain TSDoc.

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

* chore: condense env route security comments

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

* icons update

* chore(security): tighten inline comments in CSV export and KB file authorization

Condense verbose comment blocks to concise TSDoc/single-line form; no behavior change.

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

* fix(security): validate internal serve origin in KB file authorization

Replace the bypassable isInternalFileUrl substring check in resolveInternalKbKey
with an origin allow-list (base URL, internal API base URL, TRUSTED_ORIGINS).
A crafted external host whose path is /api/files/serve/<victim-key> no longer
resolves to the victim key. Relative same-origin URLs are unaffected.

* style(sso): use idiomatic sql lower() comparison for domain conflict query

Match the repo's prevailing `sql`lower(col) = value`` idiom for the
case-insensitive SSO domain conflict lookup.

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

* fix(security): align workspace env admin gate with hasWorkspaceAdminAccess

Use the same admin check the secrets UI uses (owner, admin permission, or
org-admin) so owners and org-admins are not wrongly denied their own decrypted
workspace secrets, while read-only members remain restricted to names only.

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

* fix(sso): rely on lower(domain) match for conflict detection, drop dead in-memory recheck

Address PR review: the SQL `lower(domain) = <normalized>` predicate already
excludes rows that the in-memory `normalizeSSODomain(...) === domain` recheck
claimed to catch, making that recheck dead/misleading code. Match on the
canonical lower-cased domain and filter purely by ownership. Malformed legacy
values (wildcards, schemes, ports) never match an email domain at sign-in, so
excluding them is not a gap. Test DB mock now applies the lower() predicate so
the casing-variant case is genuinely exercised.

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

* fix(security): scope webhook deploy path conflict to active webhooks

findConflictingWebhookPathOwner omitted the isActive filter that the
runtime dispatcher (findAllWebhooksForPath) applies, so an inactive but
non-archived webhook from another workflow (e.g. after undeploy or
failure auto-disable) would permanently block any new deployment on that
path even though it never receives deliveries. Align the guard with the
runtime isActive + archivedAt filter; the earliest-owner runtime check
remains the authoritative cross-tenant protection. Also trims verbose
TSDoc on the webhook path-isolation helpers.

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

* fix(security): exclude archived workflows from webhook deploy path conflict

findConflictingWebhookPathOwner now joins workflow and filters
isNull(workflow.archivedAt), matching the runtime dispatcher
(findAllWebhooksForPath). A webhook on an archived workflow can never
receive deliveries at runtime, so it must not block legitimate path reuse
with a 409.

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

* fix(security): anchor KB file ownership to earliest document in any state

A KB file's owner is now the earliest document referencing its key regardless of
state (active/archived/deleted/excluded); access is granted only when that owning
document is still active. Closes the residual where an attacker could plant an
active document to claim a file whose original document was archived or deleted.

* updated greptile icon

* revert(security): drop KB file authorization changes

Reverts the knowledge-base file-access work (origin-pinning / owner-pinning /
origin allow-list in verifyKBFileAccess) and its test. The other hardening fixes
(SSO domain registration, webhook path isolation, workspace env secrets, CSV
export) are unchanged. apps/sim/app/api/files/authorization.ts is restored to its
origin/staging baseline.

* fix(sso): treat caller's own user-scoped provider as owned during conflict check

Self-hosters often register SSO user-scoped via the CLI script (no
SSO_ORGANIZATION_ID). If they later enable organizations and reconfigure the
same domain org-scoped through the UI, the conflict check previously treated
their own user-scoped row as another tenant's and returned a misleading 409.
Recognize the caller's own user-scoped provider as owned so that migration is
allowed, while still blocking another user's or another org's domain.

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

* revert(security): remove workspace-env admin gate

Defer to a credential-based access model (separate change). Restores
GET /api/workspaces/[id]/environment to main behavior and removes the test.

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

* refactor(security): consolidate webhook path-collision check into one helper

Extract findConflictingWebhookPathOwner to lib/webhooks/utils.server.ts as
the single source of truth for cross-tenant path-collision detection, used by
both webhook creation paths (deploy sync and the manual /api/webhooks route).

This also repairs two latent issues in the manual route's previous inline
check, which queried with limit(1) and only webhook.archivedAt:
- limit(1) inspected one arbitrary row, so a same-workflow row could mask a
  foreign collision (false negative). The shared helper scans all matching
  rows.
- It omitted isActive/workflow.archivedAt, so inactive or archived-workflow
  webhooks (which never receive deliveries) permanently blocked path reuse.
  The helper mirrors the runtime dispatcher's filter.

Same-workflow webhook reuse for upsert is now a separate, explicit lookup.

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

---------

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

* fix(security): block private/reserved IPs for hosted 1Password Connect SSRF (#4818)

* fix(security): block private/reserved IPs for hosted 1Password Connect SSRF

* test(security): use real isPrivateOrReservedIP and cover IPv6 edge cases

* improvement(integrations): validate and expand devin, cursor, and greptile (#4820)

* improvement(integrations): validate and expand devin, cursor, and greptile

- devin: fix missing org_id path segment on all session endpoints, add 7 session sub-resource tools (list messages/attachments, get/append/replace tags, archive, terminate), pagination, and is_archived output
- cursor: add get_api_key_info, list_models, list_repositories tools
- greptile: align block and docs
- normalize array outputs to default [] and tighten types

* refactor(cursor): simplify list_repositories v2 array normalization

Collapse the redundant `?? []` + `Array.isArray` double-guard into a
single Array.isArray check, per PR review feedback.

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

* fix(devin): scope session-tag mapping to tag ops and normalize array tag inputs

- Only map sessionTags into the tools tags param for append/replace operations,
  preventing stale sessionTags state from clobbering create_session tags
- Fall back to a wired tags value when sessionTags is empty for tag operations
- Normalize tag inputs (string or wired string[]) via normalizeTags so array
  values from other blocks no longer throw on .split

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

* fix(cursor): restore base64 file data in legacy download_artifact metadata

The legacy CursorBlock exposes only content + metadata (no v2 file
output), so metadata.data was the only way legacy-block workflows could
access downloaded artifact bytes. Restore the base64 data field and
document it in the outputs/type instead of dropping it.

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

* fix(devin): coerce terminateArchive to archive flag for boolean-wired input

* docs(integrations): regenerate tool docs for new devin and cursor operations

---------

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

* fix(search-replace): don't auto-navigate when content edits invalidate the active match (#4819)

* fix(search-replace): don't auto-navigate when content edits invalidate the active match

* fix(search-replace): clear afterReplaceIndexRef on apply failure and zero matches

* fix(search-replace): remove duplicate setActiveSearchTarget(null) on close

* fix(search-replace): move afterReplaceIndexRef write inside handleApply past the guard

* fix(search-replace): auto-navigate when hydration resolves with no prior active match

* chore(search-replace): remove inline comments

* fix(search-replace): revert !activeMatchId guard that caused immediate re-navigation after deselect

* improvement(enrichments): limit company-info to fields both providers return (#4817)

Hunter's company dataset returns null industry/foundedYear for many large companies (verified against the live API for Microsoft, Amazon, Google), so under the first-non-empty-wins cascade those columns appeared inconsistently across rows. Limit company-info outputs to employee count and description — the fields Hunter and PDL both reliably return — so every row is consistent. employeeCount is a string so Hunter's range bucket and PDL's exact count share the column.

* fix(files): don't reject external URLs containing '..' in file parse validation (#4821)

* fix(files): don't reject external URLs containing '..' in file parse validation

The file block's file_fetch operation rejected any external URL whose path
contained '..' (e.g. Slack files-pri slugs with a literal '...') with
'Access denied: path traversal detected'. Traversal checks only apply to
local paths — external http(s) URLs are fetched with SSRF protection
downstream and are never resolved against the filesystem, so they now
short-circuit as valid. Internal /api/files/serve/ URLs keep full traversal
protection.

* test(files): fix external-URL assertion to handle undefined error

* test(files): assert success explicitly in external-URL traversal test

* fix(files): keep traversal protection for https URLs matching internal serve paths

* feat(google-sheets): add row filtering to read with numeric operators (#4822)

* feat(google-sheets): add row filtering to read with numeric operators

Adds client-side row filtering to the Google Sheets read (v2) operation.
Filter the returned rows by a header column using text operators
(contains, not_contains, exact, not_equals, starts_with, ends_with) and
numeric/ordering operators (gt, gte, lt, lte). Filtering lives in a pure,
unit-tested helper (filterSheetRows) and runs over the fetched read range;
an optional `filter` output reports whether the column was found and how
many rows matched.

Also hardens the surrounding tools:
- trim spreadsheetId in write/update/append URL builders (matches read)
- URL-encode the v1 read default range
- expose valueInputOption for the update operation in the block

Backwards compatible: with no filter requested, read output is byte-
identical and the `filter` field is omitted. The filterMatchType union is
widened additively (4 -> 10 values).

* fix(google-sheets): correct filter metadata for missing column and header-only sheets

- matchedRows is now 0 (not totalRows) when the filter column is not found,
  so it no longer contradicts applied=false / columnFound=false
- columnFound now reflects an actual header lookup for empty/header-only
  sheets instead of being hardcoded true
- add tests covering header-only and empty sheets with present/absent columns

* fix(selectors): fetch all pages for paginated dropdown list routes (#4823)

* fix(selectors): fetch all pages for paginated dropdown list routes

Dropdown selectors fetched only the first page of paginated provider
APIs, silently hiding results past page one. Add bounded server-side
draining to the list routes across Microsoft Graph, Google, Notion,
Atlassian, Linear, AWS CloudWatch, and offset/token REST APIs, plus a
shared client-side drain cap in the selector hook. Response shapes,
stored values, and tool execution are unchanged; CloudWatch list tools
still honor a caller-supplied limit. Also fixes the Word file picker
that was searching for .xlsx files.

* fix(selectors): harden JSM and Monday pagination draining

- JSM service-desk/request-type drains advance `start` by the actual row
  count returned (not the fixed page size) and stop on an empty page, so a
  short non-final page can't skip items.
- Monday boards drain now checks `response.ok` per page, surfacing a
  mid-drain HTTP failure instead of treating it as an empty final page and
  returning a partial 200.

* docs(selectors): clarify JSM drain advances start by actual row count

The offset-advancement fix (advance `start` by the rows returned, not the
fixed page size) landed in 7b19788a8; update the TSDoc to match so it no
longer reads as advancing by `limit`.

* fix(selectors): drain fetchPage in direct fetchList callers

Making `fetchList` optional left three direct callers (outside the
useSelectorOptions hook) calling it unguarded, which broke the build's
type check. Route them through a shared `loadAllSelectorOptions` helper
that uses `fetchList` when present and otherwise drains `fetchPage`.
This also prevents a regression: `confluence.spaces` / `knowledge.documents`
now paginate via `fetchPage` only, and these callers (search/replace,
value resolution) would otherwise have silently returned no options.

* chore(selectors): rename MAX_PAGE_PAGES to MAX_NOTION_PAGES for readability

* fix(sso): re-check domain conflict before write and reject IP-address domains (#4825)

* improvement(copilot): make copilot_messages the sole transcript store, remove JSONB dual-write (#4826)

Stop writing/reading the legacy copilot_chats.messages JSONB column now that
reads are cut over to copilot_messages. Make appendCopilotChatMessages the
primary write (throws on failure instead of swallowing), repoint peripheral
readers (workspace VFS, chat cleanup, data drains, fork, superuser import) to
copilot_messages, and persist the assistant turn inside finalizeAssistantTurn's
transaction so it commits atomically with the stream-marker clear. The column
itself is dropped in a follow-up migration after this bakes.

* feat(tables): expand filter operators (not-contains, starts/ends-with, not-in, empty) (#4827)

Add does-not-contain ($ncontains), starts-with ($startsWith), ends-with
($endsWith), not-in-array ($nin, previously executed server-side but unexposed
in the UI), and is-empty/is-not-empty ($empty) filter operators end-to-end —
SQL builder, condition types, query-builder converters/constants, the filter
UI, the Table tools/block descriptions, and docs.

Also fix correctness bugs in the filter builder surfaced by the wider operator
set:
- Same-column AND rules (e.g. age > 18 AND age < 65, or name startsWith 'A'
  AND name endsWith 'Z') silently overwrote each other because the AND group
  was keyed by column name. They now merge into one operator object, which
  also makes Filter -> rules -> Filter round-trip losslessly for multi-operator
  columns.
- $nin values were not split into an array like $in, and textual-match values
  like "123" were numeric-coerced (breaking the ILIKE path).
- A non-boolean $empty operand from the raw API silently inverted the check; it
  now coerces 'true'/'false' strings and otherwise returns a 400.

* improvement(copilot): stop persisting tool-call result outputs in transcripts (#4829)

Opening a Mothership task could take many seconds because a single persisted
assistant message in copilot_messages.content can reach hundreds of MB, almost
entirely inside contentBlocks[].toolCall.result.output (e.g. a get_workflow_logs
or run_workflow result). The DB query is ~2ms; the cost is detoasting that
payload, shipping it to the browser, and parsing it.

These outputs are dead weight on the Sim side: they are never rendered (the
thread shows only tool name/title/status) and never replayed to the model (the
upstream copilot service owns conversation memory). So drop result.output before
it is persisted, keeping result.success/error plus the tool metadata.

- add stripToolResultOutput() in persisted-message.ts
- apply it in messages-store toRow (covers every write path) and in
  loadCopilotChatMessages (existing rows render fast on read)

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

* feat(providers): add Together AI, Baseten, and Ollama Cloud model providers (#4830)

* feat(providers): add Together AI, Baseten, and Ollama Cloud model providers

* fix(providers): guard Ollama streaming fast-path with hasActiveTools

Match Together/Baseten/Fireworks: when tools are supplied but all are
filtered out (usageControl 'none'), take the single streaming call instead
of an extra non-streaming round-trip.

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

* fix(providers): filter non-chat model types from Together model list

* refactor(providers): dedupe Ollama Cloud upstream schema

ollamaCloudUpstreamResponseSchema was byte-for-byte identical to
ollamaUpstreamResponseSchema (both /api/tags endpoints return the same
{ models: [{ name }] } shape). Drop the duplicate and reuse the shared schema.

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

---------

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

* fix(knowledge): calendar view sync, deduplicate popover animation classes, type-safe filter cast

* cleanup(knowledge): remove TRIGGER_BORDER_CLASS duplication, inline displayLabel, drop enabledFilterParam alias

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com>

* feat(blocks): add BlockMeta to Quiver and Linq; fix invalid block config fields; update skills

Block fixes:
- Add QuiverBlockMeta (tags + 3 templates: icon generator, diagram creator, vectorizer)
- Fix QuiverBlock: remove invalid tags field from BlockConfig, IntegrationType.Design →
  IntegrationType.AI (Design doesn't exist in the enum)
- Fix GreptileBlock: remove invalid tags field from BlockConfig,
  IntegrationType.DeveloperTools → IntegrationType.DevOps
- Fix LinqBlock: remove invalid tags field from BlockConfig (tags belong only in BlockMeta)

Skills:
- add-block: add dedicated BlockMeta section with structure, rules, and registration
  pattern; add BlockMeta checklist items
- add-integration: add BlockMeta to block structure template, add rules clarifying
  that tags must NOT appear on BlockConfig and integrationType must be a valid enum
  value; update registry snippet to include blocksMeta; add checklist items

* fix(integrations): fix category dropdown by defining missing LANDING_INTEGRATIONS_DATA_PATH and regenerating integrations.json

The staging merge introduced landing-content.ts but forgot to define
LANDING_INTEGRATIONS_DATA_PATH in generate-docs.ts, causing the script
to crash before writing integrations.json.

The stale JSON had integrationTypes (plural array) from an older script
version, while the Integration type and workspace UI both read
integrationType (singular string) — so ALL_CATEGORY_SECTIONS bucketed
to undefined and the category filters never appeared in the dropdown.

Fixed by adding the missing path constant and re-running the generator.
integrations.json now has 192 entries with the correct integrationType field.

* fix(sidebar): restore resize handle on all pages

commit 3109104582 wrapped the resize handle in {(isCollapsed ||
isOnWorkflowPage) && ...} and added a useEffect that resets sidebar
width to SIDEBAR_WIDTH.MIN whenever the user navigates away from a
workflow page. Together these made the sidebar non-resizable on Tasks,
Tables, Knowledge Base, and every other non-workflow page.

Restore the staging behavior: always render the resize handle and
remove the effect that forced the width reset on page transitions.

* fix(sidebar): match staging onKeyDown and tabIndex on resize handle

The resize handle was still conditionalizing onKeyDown and tabIndex
on isCollapsed, blocking keyboard accessibility of the separator role
when expanded. Staging always attaches both unconditionally.

onKeyDown={isCollapsed ? handleEdgeKeyDown : undefined} → onKeyDown={handleEdgeKeyDown}
tabIndex={isCollapsed ? 0 : undefined}                 → tabIndex={0}

* feat(integrations): show connected credentials on integration detail page

When navigating to /integrations/google-docs (or any integration), a
Connected section now appears above the templates listing all workspace
credentials tied to that provider. Each row links back to the credential
detail page (/integrations/connected/${id}) for management actions.

Pairs with the earlier change that routes connected items from the
integrations list to the provider detail page instead of directly to
the credential detail page.

* fix(integrations): rename Add in chat to Add to Sim

* fix(skills): rename Add button to Add to Sim

* fix(platform): restore M1/M2/M3 regressions and LazyMotion on landing page

M1 — Invitation guard: re-introduce usePermissionConfig().isInvitationsDisabled
alongside the workspace inviteDisabledReason check. The flag now also
respects NEXT_PUBLIC_DISABLE_INVITATIONS and EE permission-group
disableInvitations, not just billing policy.

M2 — Settings redirects: /settings/integrations and /settings/skills
now server-redirect to /integrations and /skills respectively so old
bookmarks and emails don't silently land on General.

M3 — Starter block search exclusion: restore block.type !== 'starter'
guard in the search store so users cannot add a duplicate Starter block
via the command palette.

LazyMotion: restore LazyMotion + domMax/domAnimation wrappers and m.*
components in landing-preview-panel and landing-preview-home. The
removal was accidental (the full motion bundle was left after an
import cleanup), which caused the entire framer-motion feature set to
load eagerly on the landing page.

* fix(integrations): revert connected list to credential detail; remove settings redirects

* feat(sidebar): restore workspace switcher search with updated styling

Shows a search input in the workspace dropdown when the user has more
than 3 workspaces (WORKSPACE_SEARCH_THRESHOLD). Keyboard navigation:
ArrowDown/Up to move through results, Enter to switch, resets on close.

Styled to match the current branch (border-1/surface-5 tokens, sm text,
11px Search icon) rather than the old staging styles. Highlight state
is wired through chipVariants active prop so it follows the same active
appearance as clicked/hovered items.

* fix(sidebar): clean up workspace search — layout, memo, and effect guard

* fix(sidebar): align workspace rename input selection style with workflow rename

* perf(sidebar): eliminate React re-renders during sidebar drag resize

Previously, every mousemove during resize called setSidebarWidth(), which
both updated the --sidebar-width CSS variable (sync) and set Zustand state
(async). This caused:
  1. A 1-frame transition flash on mousedown — isResizing state had to
     round-trip through React before the is-resizing CSS class was applied,
     so the width transition fired for the first pixel of movement.
  2. A React re-render per pixel dragged — components reading sidebarWidth
     from the store (avatars, usage-indicator) lagged one frame behind the
     container, making the + and ... buttons appear to jump ahead.

New approach:
  - handleMouseDown adds is-resizing directly to the sidebar DOM node before
    any React involvement (synchronous, no frame lag).
  - mousemove writes only to the CSS custom property (zero React renders).
  - mouseup persists the final width to Zustand exactly once.
  - isResizing / setIsResizing state removed from the store and hook — they
    are no longer needed since the class is managed via direct DOM mutation.

* perf(sidebar): add requestAnimationFrame throttle to resize mousemove handler

* fix(sidebar): fix drag-right lag caused by WorkspaceChrome overflow-hidden transition

The sidebar-container's is-resizing class correctly suppressed its own width
transition, but the two wrapper divs in WorkspaceChrome both have
transition-[width]/transition-transform with a 175ms ease. The outer wrapper
also has overflow-hidden, so while the sidebar content was at the correct width
instantly, it was visually clipped by the outer wrapper which was still
animating — causing the + and ... buttons to appear to lag behind the resize
line on drag-right (not on drag-left, since shrinking doesn't clip content).

Fix: add sidebar-shell-outer/sidebar-shell-inner class names to both chrome
wrappers, and suppress their transitions via html.sidebar-resizing rule when
a drag is active. The html.sidebar-resizing class is toggled directly in the
resize hook alongside is-resizing, so it takes effect synchronously on mousedown.

* fix(icons): redesign Download icon to match Upload style; fix Upload/Download confusion

Download icon was missing the tray/shelf line at the bottom that Upload has,
making it look like a plain arrow rather than a matched pair. Updated Download
to use the same viewBox, stroke weight, and three-path structure as Upload
(tray + stem + arrowhead), just pointing down.

Also fix 5 places where Upload (↑) was incorrectly used for download/export
actions:
  - files.tsx: two Download action rows in toolbar and context menu
  - tables/table.tsx: Export CSV toolbar button
  - table-context-menu.tsx: Export CSV context menu item
  - logs.tsx: Export toolbar button
  - landing-preview-logs.tsx: decorative Export button

Import CSV and actual upload actions correctly keep the Upload icon.

* fix(icons): replace Upload with Download on all remaining export/download actions

- panel.tsx: Export workflow dropdown item
- context-menu.tsx: Export in sidebar workflow context menu
- chat.tsx: Export chat button
- output-panel.tsx: Export console CSV button
- terminal.tsx: Export console CSV button
- resource-content.tsx: Export table as CSV + Download file buttons

* fix(icons): fix remaining Upload→Download on download actions in files and logs

- action-bar.tsx: download button in files toolbar
- file-row-context-menu.tsx: Download item in file context menu
- file-download.tsx: both download buttons in log details file viewer

* updated block skills, settings pages, modals, buttons -> chips, blocks missing metadata

* updated skill modal

* improvement(resource-header): refine breadcrumb truncation ux

* improvement(resource): add floating overflow text tooltips

* wire up credits counter

* improvement(resource-header): mute path dropdown title

* refactor(resource-header): share floating-tooltip engine, prune dead overlay tooltips (#4844)

Clean up the breadcrumb truncation feature for reuse and correctness:

- Extract useFloatingTooltip / useIsOverflowing / FloatingTooltip into a shared
  floating-tooltip module. BreadcrumbSegment and FloatingOverflowText now consume
  one implementation instead of duplicating ~150 lines of positioning, velocity,
  overflow-detection, and portal logic.
- Replace the hardcoded terminal-label regex in ResourceHeader with a typed
  `terminal` flag on BreadcrumbItem (set by the document chunk/loading crumbs),
  decoupling the generic header from knowledge-base copy.
- Clear the path-popover close timeout on unmount and reuse the shared
  POPOVER_ANIMATION_CLASSES constant.
- Drop the redundant manual overflow-state writes (fixes a sticky fade mask).
- Revert FloatingOverflowText inside Combobox `overlayContent` back to plain
  truncating spans across files/logs/tables/scheduled-tasks/document: the combobox
  overlay is pointer-events-none, so the tooltip handlers never fired there.

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

* refactor(emcn,resource-header): address PR #4844 review feedback

- useIsOverflowing now uses a callback ref so the ResizeObserver follows the
  element across mount/unmount/reassignment instead of capturing it once at mount.
  Safe for conditionally rendered consumers of the shared hook. (greptile P2)
- Move POPOVER_ANIMATION_CLASSES out of chip-date-picker implementation internals
  into emcn/components/popover/popover-animation.ts, exported from the
  @/components/emcn barrel. Consumers now import from the module boundary.
  (greptile P2)

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

* upgrade table and styling upgrade

* fix schema to include integration

* fix(files): align delete icon with tables view (Trash → Trash2)

Co-Authored-By: waleed <waleed@simstudio.ai>

* fix(mothership): preserve blockType for integration contexts in sent messages

Integration mention chips were missing their provider icons in sent messages
because blockType was dropped when mapping ChatContext to messageContexts.
renderIntegrationTile returns null without blockType, silently hiding the icon.

* fix(mothership): allow 'integration' resource type in chat resources API

The VALID_RESOURCE_TYPES allowlist was missing 'integration', causing a
400 error when adding integrations to the Mothership resource tab — so
they never persisted and disappeared on refresh.

* fix(ui): Add "File" title next to file resource header

* fix(ui): fix resource header columns being bolded

* fix(resource): keep the floating tooltip from jumping on click

Gate the focus-driven show behind :focus-visible so a mouse click (which
focuses the trigger) no longer re-shows the tooltip anchored to the element's
bottom edge. On click the tooltip now hides cleanly instead of jumping down;
keyboard focus still shows it.

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

* perf(sidebar): eliminate unnecessary re-renders in workspace switcher for non-search users

- onMouseEnter: only set highlightedIndex when showSearch is true, preventing
  a state update + re-render on every workspace row hover for users with ≤ 3
  workspaces where the search is never shown
- onOpenChange: only reset workspaceSearch and highlightedIndex when showSearch
  is true, since both values are always already at their defaults for non-search
  users and setting them triggers a pointless re-render during dropdown close
- data-workspace-row-idx: only set when showSearch is true since the scroll
  effect that reads this attribute is already gated on showSearch

* feat(search): context-aware cmd-k results on the integrations page

When cmd-k is opened on the integrations page, show two new result
groups: connected accounts (visible even with empty input) and catalog
integrations (appear once the user types). Selecting an OAuth integration
deep-links to its detail page with ?connect=oauth so the connect modal
auto-opens. Non-OAuth integrations navigate to the plain detail page.

Both groups are gated to the integrations page only and respect the
hideIntegrationsTab permission. The credentials fetch shares the same
React Query cache key as the integrations page itself (no double fetch).

* refactor(emcn): make the floating tooltip the one canonical Tooltip

Replace the Radix-based emcn Tooltip with the cursor-following floating tooltip so
every tooltip in the app uses one consistent style. Built on the shared
floating-tooltip engine (relocated into emcn), not a parallel implementation.

- Move the floating-tooltip engine into emcn/components/tooltip and export it from
  the barrel; re-point its consumers (FloatingOverflowText, resource-header)
- Extend the FloatingTooltip bubble to render arbitrary children (+ role/id for
  a11y) so it can back general tooltips, not just overflow text
- Rebuild emcn Tooltip (Root/Trigger/Content/Provider/Shortcut/Preview) on
  useFloatingTooltip — compound API preserved, ~350 call sites unchanged, legacy
  side/align props accepted and ignored (the tooltip follows the cursor). Removes
  @radix-ui/react-tooltip usage (package kept for a later cleanup; react-slot
  retained for asChild)

Note: general tooltips now show instantly (no hover delay) and follow the cursor.

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

* style(emcn): put tooltip text on the design scale (text-caption)

Replace the tooltip's ad-hoc `text-xs` + `leading-[18px]` with the semantic
`text-caption` (12px) font-size token so the text styling is fully on the design
scale and self-documenting, matching how the rest of the system is set up. The
color already used the global `--text-body` token. No visual change (still 12px
with a ~18px line height).

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

* feat(sidebar): add empty task state and inline task creation

- Show "No tasks yet" in the Tasks section (expanded and collapsed) when the list is empty
- Clicking + now creates a task via the API and navigates directly to it, rather than navigating to home
- Add isCreatingTaskRef guard to prevent double-click from spawning multiple tasks
- Disable + button while creation is pending
- Fall back to home navigation on creation error

* invite, billing, home

* improvement(seats): auto purchase seats on invitations into workspace (#4857)

* improvement(seats): auto purchase seats on invitations into workspace

* improve sampling for seat drift reconciler

* address comments

* feat(knowledge): align connector UI with integrations page styling

- ConnectorTypeCard now matches integration rows: brand-colored rounded-xl tile, ArrowRight, title/subtitle hierarchy
- ConnectorCard icon upgraded from flat surface-4 to branded tile (white icon on brand bg, graceful fallback)
- Connector header badges use chipVariants instead of custom Button classes
- Add-connector search input aligned to integrations style (h-[30px], rounded-lg, border-1)

* fix(icons): trim Folder SVG viewBox to remove right-side whitespace

The folder path only extends to x≈14.33 in a 15-unit viewBox, leaving
~0.5 units of empty space on the right. At 12px rendered size this
produces ~0.4px extra gap (visible as ~1px on retina displays) compared
to solid icons like the workflow color square. Trimming the viewBox to
14.5 units makes the folder fill its chip slot evenly.

* fix(user-input): restore draft text synchronously to preserve contexts on nav

The SSR-safe approach (empty useState + effect restore) created a timing
window where the sync effect in useContextManagement fired with message=''
before the value was set, clearing any restored contexts. Folder and workflow
contexts (not re-added by applyAutoMentions) were lost on every nav-back.

Revert to the staging approach: initialize value synchronously from the
draft store so message is already populated when effects run, matching
the behavior on staging.

* fix(queue): render context chips in queued messages

- Remove plainMentions from queued message rows so context chips render
  with icons, consistent with sent messages
- Fix computeMentionRanges to use '/' prefix for skill contexts (content
  has the slash trigger restored at submit time, not '@')

* fix(mothership): remove integrations from add-resource dropdown

* fix(mothership): comment out integrations from add-resource dropdown

* chore(db): drop form, templates, template_creators, template_stars tables

These tables backed the Forms and Templates platform features which were
intentionally removed from this branch. Clean up the DB schema to match.

* chore(db): add migration metadata for 0224 drop tables

* block icons, sidebar, toolbar

* chore: remove remaining dead code for template-profile feature

- Remove 'template-profile' from SettingsSection union type
- Remove 'template-profile' entry from SECTION_TITLES
- Remove now-redundant template-profile guard in settings sidebar
- Remove commented-out template-profile nav item

* fix(multi-select): preserve anchor on range selection for tasks and folders

After a shift+click range, the anchor (lastSelectedTaskId / lastSelectedFolderId)
was being updated to the end of the range (toId). This caused subsequent
shift+clicks to extend from the wrong point instead of the original click.

Standard behavior: anchor stays at the initial click (fromId) so repeated
shift+clicks always expand/contract relative to where you started.

* feat(emcn): add SearchInput component and unify search bars platform-wide

- Add SearchInput to emcn: 30px chip-family filled search input matching the
  integrations page pattern (border-1, surface-5, leading Search icon)
- Migrate all 22 search bars across settings, EE pages, and integrations to
  SearchInput (only layout classes allowed at callsites)
- Rename Sim Keys -> Sim API Keys in nav/title; page copy now says API key
- Remove components/ui input, label, and verified-badge; migrate consumers
  to emcn equivalents or raw inputs (table cell editor, wand prompt bar)
- Delete dead EE skeleton files (data-drains, data-retention)
- General settings: Home Page chip moves to header left as navigation

* fix(files,tables): restore new-file editor autofocus and CSV import error toasts

Both were dropped on the staging line and regressed vs production (main):

- Files: the new-file editor autofocus chain (files.tsx -> file-viewer ->
  text-editor) was stripped by the react-doctor dead-code pass in #4544,
  which misread the prop-drilled `autoFocus` (consumed by an imperative
  `editor.focus()` effect) as unused. Restored the prop through all three
  layers and the one-shot focus effect so creating a new file focuses the
  editor immediately.
- Tables: CSV import failures were silently logged with no user feedback.
  Restored the per-file and generic `toast.error` surfacing.

* feat(home): score suggested actions by workspace signals

- Derive the suggestion pool from the curated block template catalog
  (1,343 prompts across 172 blocks) instead of 15 hardcoded entries
- Fix inverted relevance: prompts for connected providers are now boosted
  4x (instantly runnable) instead of excluded; unconnected discounted 0.4x
- Weight by featured (3x), popular category (1.5x), and resource gaps
  (no tables -> boost table starters; has KBs -> dampen KB-creation prompts)
- Weighted sampling without replacement, max one suggestion per block
- Connect rows weighted by catalog template count; 2 for fresh workspaces,
  1 once something is connected
- Key the catalog map by both versioned and base block types so gmail_v2
  templates resolve (gmail, github, notion, linear were silently dropped)
- Replace derive-in-effect state with a useMemo keyed by a shuffle nonce
- Add suggested_action_clicked / suggested_actions_shuffled /
  suggested_actions_toggled PostHog events

* billing, teammates

* improvement(credentials): credentials invites, secrets tab wiring up (#4874)

* improvement(credentials): move away from invite notion

* wire up secrets ui/ux

* address comments

* get consistent styling by removing emcninput + text area

* styling consistency

* remove fallback

* address comment:

* refactor(ui): migrate settings & workspace UI to chip design system

Migrate modals to ChipModal (showDivider, hint, resizable, size, leading,
ChipModalTabs), standardize Chip variants, add ChipCombobox wrapper, and apply
chip inputs/dropdowns across settings, knowledge, logs, tables, inbox, EE tabs.
Render ChipModalTabs as a ChipSwitch segmented control. Align /settings/secrets
detail with /integrations, refresh whitelabeling, and restore file-editor
autofocus and CSV import error toasts.

* fix(mothership): restore integrations to useAvailableResources for @ mention

Integrations were fully removed from useAvailableResources which broke
the @ mention menu since user-input shares the same hook. Now integrations
are always included in the hook but excluded at the AddResourceDropdown
component level, keeping them out of the sidebar + menu while remaining
available for @ mention autocomplete.

* refactor(settings): chip design-system consistency pass across all tabs

Extract a shared chip-field shell (CHIP_FIELD_SHELL/CHIP_FIELD_INPUT) mirroring
Input variant='chip' and route secrets, credential detail, and integrations
credential detail through it (30px height, font-medium, focus ring). Add a
Discard action to the secrets header when dirty. Group BYOK providers into
Models/Search & web/Enrichment sections and align its tiles to the integrations
tile.

Normalize list-row typography to text-[14px]/text-[12px] and icon tiles to
rounded-xl + border across api-keys, copilot, custom-tools, mcp,
workflow-mcp-servers, credential-sets, and access-control. Tone the secrets
Details chip and per-row affordances to ghost. Fix token correctness: raw
tailwind colors to design tokens (data-drains), missing chip variant on the
Snowflake role input, ColorInput chip-field reuse (whitelabeling), error token
and icon sizes (workflow-mcp-servers), border token (mcp), no-results sizing
(secrets), row chrome (recently-deleted), hover token and Button to Chip
(access-control), and deduped textarea chrome (sso).

* feat(settings): unify filter dropdowns on ChipSelect (integrations style)

Add a ChipSelect emcn component — a filled chip trigger + chevron opening a
DropdownMenu, matching the integrations category filter — supporting single
select, multi-select (checkbox rows), grouped options, and optional in-menu
search. Migrate every settings/EE filter dropdown off ChipCombobox to it:
audit-logs (resource-type multi + time-range), data-retention, data-drains,
general, admin (grouped tool picker), inbox status filter, and the workflow
MCP-server pickers. The SSO provider-id field stays an editable combobox since
it accepts free-text slugs.

Also fix audit findings: chip the MCP client-secret input, normalize an MCP
error-text size, and drop now-dead destination-icon code in data-drains.

* fix(mentions): require explicit @ for integration mentions; decorate sent messages robustly

- Bare integration names in prose (Monday, Notion, Clay) are no longer
  auto-converted to mentions or chipped — mention treatment is strictly
  opt-in via a token-starting @ (fixes the scunthorpe problem)
- @-prefixed mentions still canonicalize casing (@slack -> @Slack) on both
  the keystroke fast-path and bulk paths (paste, template, draft, STT)
- Sent/queued messages now self-sufficiently decorate @IntegrationName
  tokens via a text scan, covering messages sent before the input pass
  ran or authored outside the chat input
- Integration contexts missing a resolvable blockType (messages persisted
  before blockType was saved) are backfilled by label lookup so their
  mention pills render the brand icon again

* refactor(settings): section the API keys page like secrets

Wrap Workspace, Personal, and the allow-personal-keys toggle in SettingsSection
(muted label + divider) instead of bare bold headers, matching the secrets and
BYOK pages.

* fix(settings): ChipSelect renders above modals + full-width form mode

Raise the ChipSelect menu to --z-popover so it layers above modal surfaces
(--z-modal) instead of opening behind them. Add a fullWidth prop that stretches
the trigger and right-aligns the chevron for form-field use, and apply it to the
workflow MCP-server pickers.

* fix(emcn): ChipSelect uses the emcn flat chevron, not lucide's square one

The lucide ChevronDown is square; rendering it at the chip's 9x7 footprint
stretched it. Switch to the custom emcn ChevronDown (built for that wide aspect),
matching the integrations filter and ChipDropdown.

* fix(emcn): ChipSelect trigger hugs its content (w-fit)

In a stacked form layout the trigger was stretched by align-items: stretch,
leaving an empty gap to the right of the value. Add w-fit so the chip sizes to
its content (a compact pill) everywhere; fullWidth form selects are unaffected.

* fix(emcn): ChipSelect uses a square lucide chevron

Revert to lucide's ChevronDown sized square (size-[14px]) so it renders crisp,
matching the standard select chevron used by Combobox.

* renamed tasks to chats

* rename and file change

* improvement(billing): wire up billing, org, teammates tabs + remove deprecated subscription tab (#4887)

* improvement(billing): wire up billing, org, teammates tabs + remove depr subscription tab

* pass exec timeout to tool routes

* reuse helper

* address comments

* address disable comment

* chore(db): remove migration 0224 to regenerate on top of staging

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix type errors and regen migration?

* chore(db): drop branch migration 0226 ahead of staging merge; will regenerate

* chore(db): regenerate migration 0226 after staging merge

* externalize before compaction in fallback'

* fix save/discard chips to be consistent

* fix(ui): remove smodal tabs in favor of chip modal tabs

* fix(ui): skip auto-scrolling on mouse highlight of workspace

* fix(platform): restore settings redirects, forgot-password Enter submit, and tag tooltip visibility

- Re-add SETTINGS_REDIRECTS so /settings/integrations and /settings/skills
  deep links redirect to their top-level routes instead of rendering an
  empty settings panel (accidentally removed in 86da193cc3 one minute
  after cca5054cf6 added it)
- Add opt-in onSubmit to ChipModalField input/email variants and wire it
  in the forgot-password modal so Enter submits again (lost in the
  ChipModal conversion)
- Knowledge tag tooltip: drop the max-h/overflow-y-auto clamp that the
  pointer-events-none floating tooltip made unreachable; truncate each
  tag row instead so all tags stay visible with bounded height

* chore(db): remove migration 0226_third_spot before staging merge

* chore(db): regenerate migration as 0227 after staging merge

* feat(telemetry): add posthog + audit coverage for new platform actions

Audit log (compliance/permission-relevant only):
- org_seat.provisioned — seat auto-purchased when an invite acceptance
  grows the org (actor = accepting user, includes seat delta)
- org_plan.converted — Pro→Team conversion triggered by invite acceptance
- org_seat.drift_reconciled — hourly cron healed a drifted seat count
- credential_member.added/removed/role_changed — credential sharing
  surface was previously fully unaudited
- table.created — parity with existing table.updated/deleted
- skill updates now record skill.updated instead of mislabeled
  skill.created

PostHog:
- seats_provisioned, credential_shared/unshared,
  environment_updated/deleted (key counts only, never names/values)
- table_import_started/completed — background CSV imports previously had
  zero failure observability
- table_exported, file_downloaded, skill_updated
- credential_connected now fires for OAuth completions (draft-hooks),
  credential_deleted for OAuth disconnects; previously only manual
  credentials were tracked
- table_workflow_run gains deployment_mode (live/deployed/mixed)

Also: logger.warn on all new credential-admin 403 denials (members +
environment routes); invite-created audit enriched with
enforcedFixedSeats/plan.

Deliberately excluded as noise: per-hour dead-letter audit rows (would
re-record the same stuck event every cron run) and a duplicate
system-actor org_plan.converted in the Stripe outbox handler.

* feat(home): fill textarea on suggested prompt click instead of sending

Clicking a prompt action in the Suggested Actions panel now populates
the Mothership user-input textarea (via applyAutoMentions) and focuses
it with the caret at end, rather than immediately submitting. The user
can review, edit, and send manually.

* fix(templates): name owning integration in featured block template prompts

Four featured prompts omitted their owning integration name, making them
unbranded and disconnected from their title. Each prompt now explicitly
names GitHub or Google Sheets so mentionifyIntegrations renders the chip
and the copy reads as a self-contained agent-building instruction.

* fix(templates): rewrite fragment prompts so each names its integration and reads as a complete instruction

Featured and non-featured block template prompts that either omitted
the owning integration's canonical name or were phrased as marketing
fragments rather than natural user instructions have been rewritten.
Each updated prompt now starts with an imperative verb ("Build a
workflow that…"), names the owning integration explicitly so the
@-mention chip renders correctly, and aligns with the entry's title.

Files changed: salesforce, hubspot (×2), github, slack, airtable,
firecrawl, iam.

* fix(templates): name integration in remaining block template prompts

- google_docs.ts: replace 'Google Doc' with 'Google Docs document' in 4 prompts; update title 'Meeting notes to Google Doc' to 'Meeting notes to Google Docs'
- google_sheets.ts: replace 'Google Sheet' with 'Google Sheets spreadsheet/Google Sheets' in 3 non-featured prompts
- slack.ts: replace 'Google Doc' with 'Google Docs document' in 'Daily standup summary'
- stripe.ts: replace 'Google Sheet'/'Slacks' with 'Google Sheets'/'Slack' in 'Weekly metrics report'
- reddit.ts: add 'Reddit' to 3 prompts that only referenced subreddits
- notion.ts: rewrite featured prompt to start with a verb and name Notion
- jira.ts: rewrite featured marketing-fragment prompt to start with a verb
- linear.ts: rewrite featured marketing-fragment prompt to start with a verb
- gmail.ts: rewrite featured marketing-fragment prompt to start with a verb and name Gmail

* fix(icons): convert monochrome dark brand icons to currentColor for dark mode

LinkupIcon, InfisicalIcon, IntercomIcon, LumaIcon, GranolaIcon, OnePasswordIcon,
and RailwayIcon were hardcoded to black/near-black fills/strokes, making them
invisible in bare DARK mode. Convert all to currentColor so they follow the
theme-aware text color.

Add iconColor: '#286efa' to IntercomBlock (Intercom brand blue is a confident
mid-tone, safe on both themes).

Two-tone icons (StagehandIcon, AgentPhoneIcon, QuiverIcon) are left unchanged
because their white details are structural — converting to single-tone would
destroy logo legibility.

* removed color, user input, sidebar, suggested actions, chips/emcn

* removed color migration

* improve(blocks): audit block catalog metadata for accuracy and fill gaps

- Fix template prompts that claimed capabilities blocks don't have
  (fabricated triggers, non-existent tools) across ~98 integrations
- Normalize integration tags to family conventions and valid union values
- Align alsoIntegrations and modules with template prompt content
- Add BlockMeta for circleback, imap, and rss trigger integrations
- Add templates to clickhouse and greptile metas
- Remove duplicate PageSpeed deploy-gate template

* chore(telemetry): drop org_seat.drift_reconciled audit

System self-heal bookkeeping doesn't belong in the user-facing audit
trail — membership and seat-purchase changes are already audited, and
the cron's logger output covers ops visibility.

* chore(db): consolidate branch migrations into single 0227

* fix(emcn): make ChipModal scroll internally when content exceeds viewport

The Modal→ChipModal migration dropped the old ModalBody scroll container:
ChipModal renders ModalContent bare (no overflow-hidden) and its wrappers
had no min-h-0 chain, so tall modals (e.g. New data drain with an S3
destination) overflowed max-h-[84vh] off-screen with no way to scroll.

Complete the flex min-h-0 chain through ChipModal's frame and give
ChipModalBody flex-1 min-h-0 overflow-y-auto — header/footer stay pinned,
body scrolls only when constrained. Short modals are unaffected (max-h
caps, it doesn't stretch), and dropdowns inside the body are Radix-portaled
so the new scroll container cannot clip them. Five modals that had locally
patched this with max-h-[Nvh] overrides keep working unchanged.

* fix(emcn): don't close modal when dismissing a dropdown via outside click

Radix dispatches pointer-down-outside to every open dismissable layer at
once, so clicking outside an open dropdown/select inside a modal closed
both the dropdown and the modal in one jarring step. ModalContent now
prevents its own dismissal while a portaled popper layer is open — the
first outside click closes just the popper, the next one closes the
modal.

* docs(skills): de-duplicate and correct agent docs; canonical styling tokens; mirror sim-sandbox rule

* docs(skills): broaden boundary-raw-fetch scope note, sync cursor rule mirrors

* fix(emcn): harden modal popper guard and exempt caret-anchored dropdowns from body scroll

Adversarial review follow-ups to the ChipModal scroll + dismissal fixes:

- Popper guard now requires data-state="open" inside the popper wrapper,
  so a dropdown that is merely animating closed no longer swallows the
  next outside click on the modal (DropdownMenu has an exit animation
  that keeps its wrapper mounted briefly)
- Port the same guard to SModalContent for consistency
- custom-tool-modal: opt its body out of the chrome scroll container
  (flex-none + overflow-visible); the caret-anchored EnvVar/Tag
  autocomplete dropdowns are absolute-positioned inside the body and
  must spill past its bounds rather than clip against a scroll boundary

* refactor(billing): drop unnecessary useCallback wrappers from event handlers

* fix(data-drains): complete chip migration of destination forms and fill missing placeholders

Finishes the in-flight FormField → ChipModalField conversion for all
destination form specs and adds the placeholders that several inputs
never had (S3 bucket/region/access keys, Azure account key, Datadog API
key, webhook signing secret/bearer token) — the cause of the New Data
Drain modal showing placeholder-less inputs inconsistently.

* fix(emcn): broaden modal popper guard to onInteractOutside

Covers the focusOutside dismissal path too: when a popper's focus scope
unwinds on close, the transient focus shift could still dismiss the
modal (and simultaneous body pointer-events lock teardown could freeze
the page). Same data-state="open" scoping as the pointer guard.

* fix(emcn): harden modal outside interactions

* update audit mock

---------

Co-authored-by: andres <k62hc5kjst@privaterelay.appleid.com>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: waleed <waleed@simstudio.ai>
2026-06-06 11:39:48 -07:00
Theodore Li 24a6086d95 feat(tables): fractional order keys for O(log n) row insert/delete (flag-gated, default off) (#4890)
* feat(tables): add order_key column, fractional-indexing util, and ordering flag (off)

* feat(tables): write order_key on insert, flag-gate delete reindex + query ordering, add backfill

Flag off (default) = identical behavior. Single-insert assigns a fractional
order_key; queryRows orders by order_key when the flag is on; deletes skip the
O(N) reindex when on. Per-table-atomic backfill script populates existing rows.

* feat(tables): write order_key on all insert paths (batch, upsert, replace, import, create, copilot)

Completes the always-write-keys prerequisite: every row insert now assigns a
fractional order_key consistent with position order, so the flag can be flipped
safely after backfill. Flag off (default) still = identical behavior.

* feat(tables): insert-by-neighbor-id + orderKey on wire + client order-by-key

Inserts express intent as afterRowId/beforeRowId (O(1) key mint via the
(table_id,order_key,id) index); orderKey is returned on every row; client
reconcile/undo place by orderKey (no neighbor bump) with position fallback.
Flag off = unchanged. 205 table tests pass.

* feat(tables): resolve position-based inserts by key ordinal under the flag

Position-based callers (mothership tool, v1 API, undo fallback, transient old
clients) resolve their insert neighbor by order_key ordinal (OFFSET) when the
flag is on — positions are gappy then, so WHERE position=N would miss. Flag off
keeps the indexed position lookup. The mothership tool itself is unchanged.

* test(tables): flag-on coverage — delete skips reindex, insert mints key + no shift

* fix lint

* chore(db): regenerate order_key migration with default drizzle name

* fix(tables): address review — guard neighbor insert + mutual-exclusion + safe reconcile

- resolveInsertByNeighbor throws when the anchor row is missing (was silently
  inserting at the front) and when its order_key is null under the flag.
- insert contract: afterRowId/beforeRowId are mutually exclusive (refine).
- reconcileCreatedRow only key-sorts when every cached row is keyed, so mid-
  backfill un-keyed rows aren't yanked to the front.

* fix(kb): restore non-null guard in storage-key filter (unsafe-lint regression)

* refactor(tables): extract maxOrderKey + thread import append key

- Extract maxOrderKey(executor, tableId) helper; replaces three identical
  max(order_key) selects (single/batch insert append + import).
- Import: read the append anchor once up front and thread each batch's last
  key forward (nextImportStartOrderKey + afterOrderKey) instead of re-scanning
  max(order_key) per batch over a growing table — one scan per import, not
  one per 1k-row batch.

* fix(tables): keep insert body base omittable for v1 contract

The afterRowId/beforeRowId mutual-exclusion .refine() turned the schema into a
ZodEffects, which Zod forbids .omit() on — v1's insertTableRowBodySchema.omit({
position }) threw at module load (runtime-only; tsc misses it). Split the plain
object base out, apply the shared refine on top, and have v1 omit from the base
then re-apply it.

* fix(tables): chunk backfill order-key writes

A single UPDATE … FROM (VALUES …) over a whole large table overflows the JS call
stack while drizzle assembles the VALUES list (and would blow past Postgres's
65535 bound-param limit at ~32k rows) — large tables failed with 'Maximum call
stack size exceeded'. Write in 1000-row chunks inside the same per-table
transaction so keying stays atomic.

* fix(tables): emit orderKey in insert responses

The single-row and batch insert handlers dropped orderKey from the JSON
response even though the service returns it, so reconcileCreatedRow always fell
back to position-sorting and could place neighbor inserts wrong under the
fractional-ordering flag. Serialize orderKey alongside position.

* fix(tables): restore by orderKey, not position, under fractional flag

A saved position is the gappy column value, but under the flag insert reads
position as a visual rank (OFFSET) — so position-based restore misplaces rows.

- create-row redo now goes through the batch path carrying the saved orderKey
  (the single-insert API has no orderKey field); drop the now-unused single
  create mutation.
- resolveBatchInsertOrderKeys appends under the flag instead of feeding gappy
  positions to resolveInsertOrderKey; positions remain the flag-off path.

* perf(tables): backfill writes 5000 rows/chunk (was 1000)

5x fewer round-trips per table; ~10k bound params stays well under Postgres's
65535 ceiling and far below the single-statement size that overflows the stack.

* fix(tables): drop rowNumber from table trigger payload

position is gappy under the fractional-ordering flag, so rowNumber (= row.position)
no longer reflects a contiguous visual rank. Rather than compute-on-read, remove
it from the trigger payload, output schema, and column-execution input.

Also pin isTablesFractionalOrderingEnabled=false in update-row.test.ts so its
flag-off position-shift assertions are deterministic regardless of local env.

* chore(db): format generated 0226 migration metadata

biome check . flagged the drizzle-generated _journal.json and 0226_snapshot.json;
apply the formatter so packages/db lint:check passes in CI.

* docs(triggers): drop rowNumber from table trigger outputs

rowNumber was removed from the table trigger payload; remove it from the
documented output fields to match.

* test(tables): remove flag-on fractional-ordering unit suite

Flag-on behavior is covered by manual large-table verification; the heavily-
mocked DB-chain suite added little signal.
2026-06-05 17:18:33 -04:00
WaleedandClaude Opus 4.8 16954e46fe feat(integrations): add ClickHouse block and expand Dagster + Tinybird tools (#4883)
* feat(integrations): add ClickHouse block and expand Dagster + Tinybird tools

* fix(tinybird): fail loudly on invalid query_pipe parameters JSON

parsePipeParameters previously returned {} on any JSON parse error, so a
mistyped 'parameters' input produced a successful pipe call with the dynamic
filters silently dropped. Throw a clear error for non-empty, non-object input
instead; an omitted/empty value still means 'no parameters'.

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

* fix(dagster): guard NaN numeric coercions and bound list_assets pagination

Address PR review:
- Route all block numeric coercions (list_runs limit/createdAfter/createdBefore,
  get_run_logs logsLimit, list_assets assetsLimit) through a toFiniteNumber()
  guard so invalid/wand-generated text becomes undefined instead of NaN.
- list_assets now applies a default page size (100) when no limit is given, so
  paging stays bounded and hasMore is meaningful even when limit is omitted.

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

* fix(dagster): make list_assets hasMore exact via fetch N+1

Address PR review (hasMore true on exact page): request one extra row
(pageSize + 1), use its presence as the authoritative hasMore, slice it off,
and derive the returned cursor from the last RETURNED asset's key path
(JSON-serialized; Dagster normalizes JS/Python whitespace on the way in).
This removes the false-positive hasMore when the final page is exactly full.

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

* fix(clickhouse): enforce read-only query operation and harden WHERE-clause guard

* fix(dagster): make list_runs hasMore exact via fetch N+1

Address PR review (list runs false hasMore): request one extra row
(pageSize + 1), use its presence as the authoritative hasMore, and slice it
off before mapping. Removes the false-positive hasMore (and misleading cursor)
when the final page is exactly `limit` runs long. Mirrors the list_assets fix.

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

* fix(clickhouse): restrict DROP PARTITION to literal values to prevent SQL injection

* fix(clickhouse): reject chained statements in read-only query operation

* fix(clickhouse): force JSON output on query path and ignore comments when detecting chained statements

* fix(tinybird): encode datasource/pipe names in URL paths to prevent traversal

A user-or-llm datasource/pipe name interpolated raw into the URL path (e.g.
'real_ds/../../other') is normalized by the WHATWG URL parser and can target a
different endpoint. Wrap the path segment with encodeURIComponent in the
truncate, delete, and query_pipe URLs. Events/append pass the name via
URLSearchParams, which already encodes, so they were unaffected.

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

* fix(clickhouse): block WITH-led writes/DDL in read-only query operation

* fix(clickhouse): validate column types structurally and normalize FORMAT around SETTINGS

* fix(clickhouse): balance-check ORDER BY/PARTITION BY and skip leading comments in read-only guard

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 14:49:51 -07:00
Waleed 2ccd9a3ac5 feat(connectors): add 7 knowledge base connectors (Google Forms, Typeform, Azure DevOps, YouTube, JSM, S3, Sentry) (#4880)
* feat(connectors): add 7 knowledge base connectors (Google Forms, Typeform, Azure DevOps, YouTube, JSM, S3, Sentry)

* fix(connectors): tighten listingCapped semantics per review (WIQL cap, batch omissions, cap-vs-exhaustion)

* fix(connectors): google-forms listingCapped must fire on slice regardless of hitLimit (404-null-filter gap)

* fix(connectors): s3 streaming size cap for chunked responses without content-length

* fix(connectors): ado byte-exact file content fetch, google-forms hash-poisoning on listing failure

* fix(connectors): ado auth-failure deletion guard, jsm last-page slice flag, google-forms response cap in hash

* fix(connectors): shared streaming size-cap reader for ado file hydration (promote from s3)

* fix(knowledge): flag incomplete listings at engine level when pagination is truncated

* fix(connectors): ado flags listing incomplete when a non-empty repo has no resolvable branch

* fix(knowledge): engine truncation flag is an absolute deletion block (fullSync cannot override); s3 byte-exact size fallback; ado tsdoc accuracy

* improvement(knowledge): extract shouldReconcileDeletions gate as tested pure function, tighten engine comments

* test(connectors): mapTags coverage for the 7 new connectors

* fix(connectors): ado probes past the wiql 20k cap before flagging; document custom-wiql full-listing behavior

* fix(connectors): ado flags partial repo trees when items listing emits a continuation token

* fix(connectors): ado discards foreign-phase cursors; google-forms scans all response pages for change detection

* fix(connectors): audit fixes across new connectors

- registry: register x connector (was dead code, never wired in)
- google-docs/google-drive/google-forms: gate deletion reconciliation on
  Drive incompleteSearch; google-docs also now sets listingCapped on its
  maxDocs cap path
- jsm: add read:jira-user scope so reporter resolves on requests
- gong: only set listingCapped on genuine truncation, not exact-cap
  source exhaustion
- gitlab: issues phase switched to keyset pagination (removes ~50k
  offset ceiling), matching the repo-tree phase
- grain: parallelize recording + transcript fetch in getDocument
- ashby: document updatedAt-based content-hash limitation for
  notes/feedback change detection
- tests: mapTags coverage for x, granola, greenhouse, fathom, rootly
2026-06-04 14:49:41 -07:00
WaleedandClaude Opus 4.8 b329c36b1a fix(auth): link SSO sign-in to existing same-email accounts (#4866)
* fix(auth): link SSO sign-in to existing same-email accounts

SSO sign-ins failed with "account not linked" (then a cascading "Invalid
callbackURL") when an account with the same email already existed. Better
Auth's `@better-auth/sso` plugin hardcodes the provisioned user's
`emailVerified: options?.trustEmailVerified ? <claim> : false`, so with the
option unset every SSO login arrived unverified and tripped the account
linking gate `(!isTrustedProvider && !userInfo.emailVerified)` whenever the
provider was not in `accountLinking.trustedProviders`.

- Set `trustEmailVerified: true` on the SSO plugin so the IdP's verified-email
  claim is honored (Okta, Entra ID, Google Workspace, Auth0 all assert it).
- Trust the operator's configured provider for linking: merge
  `SSO_PROVIDER_ID` (when present in the app env) plus a new
  `SSO_TRUSTED_PROVIDER_IDS` list into `trustedProviders`. Empty/unset =>
  no-op, so existing deployments are unchanged.
- Invite callback URL: return a clean `/invite/<id>` (token already persists
  in sessionStorage) so an appended `?error=` cannot produce a malformed URL.
- Document `SSO_TRUSTED_PROVIDER_IDS` in SSO docs, Helm values, and schema.

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

* fix(auth): address review — guard trusted SSO providers, revert invite callback

- Only compute additionalTrustedSsoProviders when SSO_ENABLED, so
  trustedProviders is exactly unchanged for non-SSO deployments.
- Revert the invite getCallbackUrl change: keep the token in the callback URL
  (with sessionStorage/searchParams fallback) so the token survives when
  sessionStorage is unavailable. The account-linking fix removes the
  "account not linked" error that caused the malformed callback URL, so the
  callback cleanup is unnecessary.

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

* fix(auth): guard trusted SSO providers with isSsoEnabled (isTruthy)

env.SSO_ENABLED can be the string "false" (t3-env returns strings for
booleans), which is truthy in JS. Use the canonical isSsoEnabled flag
(isTruthy(env.SSO_ENABLED)) so SSO_ENABLED="false"/"0" correctly yields an
empty trusted-provider list, matching how SSO is gated elsewhere.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 12:00:04 -07:00
Waleed b98fbfebef docs(slack): remove archival reference from Download files per Slack Marketplace guidelines (#4867) 2026-06-03 11:46:13 -07:00
Waleed 648a5a117d feat(storage): support S3-compatible endpoints (R2, MinIO, B2) for file storage (#4865)
* feat(storage): support S3-compatible endpoints (R2, MinIO, B2) for file storage

Add S3_ENDPOINT and S3_FORCE_PATH_STYLE env vars, wired into the shared upload
S3 client so Cloudflare R2, MinIO, Backblaze B2, and other S3-compatible stores
work for self-hosted file storage. The endpoint is trusted operator config (no
SSRF/HTTPS gate). Makes the multipart Location fallback endpoint-aware, extends
the S3 client unit tests, and documents the new vars in Helm values, .env.example,
and the English self-hosting docs (incl. browser-reachability + CORS guidance).

* docs(storage): add RustFS as an S3-compatible provider example

* fix(storage): address review feedback and fix env mock for CI

- Add envBoolean to the shared env test mock (createEnvMock) so config.ts's
  forcePathStyle coercion resolves — fixes failing knowledge/utils.test.ts
- Declare S3_FORCE_PATH_STYLE as z.string() (every other env var's pattern);
  it's coerced via envBoolean at the consumption site, avoiding a boolean
  type that never matches the string process.env value
- Log path-style from S3_CONFIG.forcePathStyle (envBoolean) instead of a
  separate isTruthy call, so the startup log can't disagree with the client
- Make buildObjectFallbackUrl honor forcePathStyle: virtual-hosted-style URL
  (bucket as subdomain) for R2, path-style only when forcePathStyle is set

* docs(storage): add backlinks to S3-compatible providers (R2, MinIO, Ceph, B2, RustFS) and backends
2026-06-03 11:37:29 -07:00
Waleed 12db7da938 feat(connectors): add 11 knowledge base connectors (#4849)
* feat(connectors): add 11 knowledge base connectors (Gong, Grain, Fathom, Granola, incident.io, Rootly, Ashby, Greenhouse, DocuSign, Monday, GitLab)

* fix(granola): restore correct calendar_event field names (event_title/organiser/calendar_event_id/scheduled_start_time/scheduled_end_time/invitees)

* fix(connectors): correct Grain id field/include flags and incident.io timestamp value shape (verified vs raw API specs)

* feat(connectors): add scoping filters for Gong (host users), Fathom (meeting type/domain), Granola (folder/created-after)

* feat(connectors): add verified scoping filters (Grain, Rootly, Ashby, Greenhouse, DocuSign, GitLab) + clarify Monday board scope

* fix(connectors): docusign sandbox source URL, greenhouse scorecard cap, gong overlap; add incident.io status/mode filters

* chore(connectors): remove non-TSDoc inline comments

* fix(fathom): apply 14-day overlap to incremental created_after so late transcripts are recaptured

* fix(fathom): cache sourceUrl in header so getDocument preserves it; return concrete contentHash (drop omit/cast)

* fix(connectors): grain check transcript before formatting; gong cache date window across cursor pages

* fix(connectors): fathom skip meetings with no transcript/summary; docusign cache from_date across cursor pages

* fix(fathom): return metadata from getDocument (carried via header cache) for tag consistency

* fix(connectors): grain cache filter window across pages, greenhouse guard NaN page, rootly skip empty incidents

* feat(connectors): final audit — add verified scoping filters (grain team/type, granola+greenhouse createdBefore, gitlab milestone, rootly service/team/env), fix incident.io paused category + rootly severity-slug copy
2026-06-02 16:57:30 -07:00
Waleed e2c2d9add3 feat(apify): add run task, get dataset items, and get run tools (#4851)
* feat(apify): add run task, get dataset items, and get run tools

* fix(apify): guard undefined dataset id and forward explicit offset=0
2026-06-02 12:54:51 -07:00
Waleed e5a46d7959 feat(linq): add Linq iMessage/SMS/RCS integration (34 tools, block, attachment upload) (#4831) 2026-06-01 13:44:33 -07:00
Waleed 919fa5274b feat(tables): expand filter operators (not-contains, starts/ends-with, not-in, empty) (#4827)
Add does-not-contain ($ncontains), starts-with ($startsWith), ends-with
($endsWith), not-in-array ($nin, previously executed server-side but unexposed
in the UI), and is-empty/is-not-empty ($empty) filter operators end-to-end —
SQL builder, condition types, query-builder converters/constants, the filter
UI, the Table tools/block descriptions, and docs.

Also fix correctness bugs in the filter builder surfaced by the wider operator
set:
- Same-column AND rules (e.g. age > 18 AND age < 65, or name startsWith 'A'
  AND name endsWith 'Z') silently overwrote each other because the AND group
  was keyed by column name. They now merge into one operator object, which
  also makes Filter -> rules -> Filter round-trip losslessly for multi-operator
  columns.
- $nin values were not split into an array like $in, and textual-match values
  like "123" were numeric-coerced (breaking the ILIKE path).
- A non-boolean $empty operand from the raw API silently inverted the check; it
  now coerces 'true'/'false' strings and otherwise returns a 400.
2026-05-31 22:49:15 -07:00
WaleedandClaude Opus 4.8 20a8d85e15 improvement(integrations): validate and expand devin, cursor, and greptile (#4820)
* improvement(integrations): validate and expand devin, cursor, and greptile

- devin: fix missing org_id path segment on all session endpoints, add 7 session sub-resource tools (list messages/attachments, get/append/replace tags, archive, terminate), pagination, and is_archived output
- cursor: add get_api_key_info, list_models, list_repositories tools
- greptile: align block and docs
- normalize array outputs to default [] and tighten types

* refactor(cursor): simplify list_repositories v2 array normalization

Collapse the redundant `?? []` + `Array.isArray` double-guard into a
single Array.isArray check, per PR review feedback.

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

* fix(devin): scope session-tag mapping to tag ops and normalize array tag inputs

- Only map sessionTags into the tools tags param for append/replace operations,
  preventing stale sessionTags state from clobbering create_session tags
- Fall back to a wired tags value when sessionTags is empty for tag operations
- Normalize tag inputs (string or wired string[]) via normalizeTags so array
  values from other blocks no longer throw on .split

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

* fix(cursor): restore base64 file data in legacy download_artifact metadata

The legacy CursorBlock exposes only content + metadata (no v2 file
output), so metadata.data was the only way legacy-block workflows could
access downloaded artifact bytes. Restore the base64 data field and
document it in the outputs/type instead of dropping it.

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

* fix(devin): coerce terminateArchive to archive flag for boolean-wired input

* docs(integrations): regenerate tool docs for new devin and cursor operations

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 18:53:29 -07:00
WaleedandClaude Opus 4.8 a8f86c0c83 fix(security): harden SSO domain registration, webhook path isolation, and CSV export (#4813)
* fix(security): harden KB file access, SSO domain registration, webhook path isolation, env secrets, and CSV export

* fix(sso): scope domain conflict query with indexed lower(domain) filter

Address PR review: avoid a full-table scan on every SSO provider
registration by filtering candidate rows in SQL with
lower(domain) = <normalized>, keeping the in-memory ownership check.
Also tighten the normalizeSSODomain TSDoc.

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

* chore: condense env route security comments

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

* icons update

* chore(security): tighten inline comments in CSV export and KB file authorization

Condense verbose comment blocks to concise TSDoc/single-line form; no behavior change.

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

* fix(security): validate internal serve origin in KB file authorization

Replace the bypassable isInternalFileUrl substring check in resolveInternalKbKey
with an origin allow-list (base URL, internal API base URL, TRUSTED_ORIGINS).
A crafted external host whose path is /api/files/serve/<victim-key> no longer
resolves to the victim key. Relative same-origin URLs are unaffected.

* style(sso): use idiomatic sql lower() comparison for domain conflict query

Match the repo's prevailing `sql`lower(col) = value`` idiom for the
case-insensitive SSO domain conflict lookup.

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

* fix(security): align workspace env admin gate with hasWorkspaceAdminAccess

Use the same admin check the secrets UI uses (owner, admin permission, or
org-admin) so owners and org-admins are not wrongly denied their own decrypted
workspace secrets, while read-only members remain restricted to names only.

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

* fix(sso): rely on lower(domain) match for conflict detection, drop dead in-memory recheck

Address PR review: the SQL `lower(domain) = <normalized>` predicate already
excludes rows that the in-memory `normalizeSSODomain(...) === domain` recheck
claimed to catch, making that recheck dead/misleading code. Match on the
canonical lower-cased domain and filter purely by ownership. Malformed legacy
values (wildcards, schemes, ports) never match an email domain at sign-in, so
excluding them is not a gap. Test DB mock now applies the lower() predicate so
the casing-variant case is genuinely exercised.

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

* fix(security): scope webhook deploy path conflict to active webhooks

findConflictingWebhookPathOwner omitted the isActive filter that the
runtime dispatcher (findAllWebhooksForPath) applies, so an inactive but
non-archived webhook from another workflow (e.g. after undeploy or
failure auto-disable) would permanently block any new deployment on that
path even though it never receives deliveries. Align the guard with the
runtime isActive + archivedAt filter; the earliest-owner runtime check
remains the authoritative cross-tenant protection. Also trims verbose
TSDoc on the webhook path-isolation helpers.

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

* fix(security): exclude archived workflows from webhook deploy path conflict

findConflictingWebhookPathOwner now joins workflow and filters
isNull(workflow.archivedAt), matching the runtime dispatcher
(findAllWebhooksForPath). A webhook on an archived workflow can never
receive deliveries at runtime, so it must not block legitimate path reuse
with a 409.

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

* fix(security): anchor KB file ownership to earliest document in any state

A KB file's owner is now the earliest document referencing its key regardless of
state (active/archived/deleted/excluded); access is granted only when that owning
document is still active. Closes the residual where an attacker could plant an
active document to claim a file whose original document was archived or deleted.

* updated greptile icon

* revert(security): drop KB file authorization changes

Reverts the knowledge-base file-access work (origin-pinning / owner-pinning /
origin allow-list in verifyKBFileAccess) and its test. The other hardening fixes
(SSO domain registration, webhook path isolation, workspace env secrets, CSV
export) are unchanged. apps/sim/app/api/files/authorization.ts is restored to its
origin/staging baseline.

* fix(sso): treat caller's own user-scoped provider as owned during conflict check

Self-hosters often register SSO user-scoped via the CLI script (no
SSO_ORGANIZATION_ID). If they later enable organizations and reconfigure the
same domain org-scoped through the UI, the conflict check previously treated
their own user-scoped row as another tenant's and returned a misleading 409.
Recognize the caller's own user-scoped provider as owned so that migration is
allowed, while still blocking another user's or another org's domain.

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

* revert(security): remove workspace-env admin gate

Defer to a credential-based access model (separate change). Restores
GET /api/workspaces/[id]/environment to main behavior and removes the test.

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

* refactor(security): consolidate webhook path-collision check into one helper

Extract findConflictingWebhookPathOwner to lib/webhooks/utils.server.ts as
the single source of truth for cross-tenant path-collision detection, used by
both webhook creation paths (deploy sync and the manual /api/webhooks route).

This also repairs two latent issues in the manual route's previous inline
check, which queried with limit(1) and only webhook.archivedAt:
- limit(1) inspected one arbitrary row, so a same-workflow row could mask a
  foreign collision (false negative). The shared helper scans all matching
  rows.
- It omitted isActive/workflow.archivedAt, so inactive or archived-workflow
  webhooks (which never receive deliveries) permanently blocked path reuse.
  The helper mirrors the runtime dispatcher's filter.

Same-workflow webhook reuse for upsert is now a separate, explicit lookup.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 17:35:48 -07:00
WaleedandClaude Opus 4.8 c6d500d841 fix(icons): repair broken integration icon rendering (#4810)
* fix(icons): repair broken integration icon rendering

Two distinct bugs left integration icons broken on the /integrations page
(visible at 32-40px, hidden at the toolbar's 16px):

1. Corrupted SVG paths (Notion, Greptile, Granola, Calendly, Grafana, Bedrock):
   over-minified data dropped elliptical-arc flag digits (e.g. `A1 1 0 5.9 7`
   instead of `A1 1 0 0 0 5.9 7`); Granola's cubic stream was truncated. Browsers
   abort path parsing at the first invalid arc flag, so each rendered as a fragment
   or blank. Replaced with correct path data from canonical sources, preserving each
   icon's existing fill/gradient and bgColor.

2. Invisible glyph (Bright Data): its icon uses fill='currentColor' but bgColor was
   '#FFFFFF', and every surface forces text-white on the glyph - white-on-white.
   Changed bgColor to Bright Data's brand blue (#3d7ffc) so the white glyph reads,
   matching the white-glyph-on-brand-chip convention.

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

* fix(icons): restore Calendly dual-tone brand colors

Addresses review feedback: the previous fix replaced the broken Calendly icon
with a monochrome #006BFF path, dropping the cyan #0ae8f0 accent from the
original dual-tone mark. Restored the two-tone logo (blue + cyan) using clean,
valid path data, cropped to a tight square viewBox so it fills the chip.

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

* improvement(icons): enlarge icons, fix Zoom contrast and Quiver chip

- Zoom: glyph was blue-on-blue (#0B5CFF on #2D8CFF chip); switched to
  currentColor so it renders as a white glyph on the blue chip.
- Quiver: chip bgColor #000000 -> #FFFFFF to match the icon's near-white box,
  and enlarged the mark slightly (viewBox crop).
- Enlarged (tightened viewBox, verified no clipping): RevenueCat, Prospeo,
  Granola, Firecrawl, Enrich.so, and the AWS icons (RDS, DynamoDB, SQS,
  CloudFormation, Athena, CloudWatch, SES, Bedrock, S3).
- ZoomInfo left unchanged: it is a full red rounded-square logo that already
  fills its frame, so a crop would clip it.

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

* fix(icons): use Bright Data wordmark on white chip; repair Circleback

- Bright Data: replaced the flame glyph with the official two-tone 'bright data'
  wordmark (provided asset), centered in a symmetric viewBox. Reverted the chip
  bgColor from #3d7ffc to #FFFFFF since the blue wordmark is invisible on a blue
  chip (the wordmark is designed for a light background).
- Circleback: a minifier had rounded the pattern's image scale to scale(0),
  collapsing the embedded logo to zero size (invisible). Restored the correct
  scale (1/280 = 0.00357142857) so the C. mark renders.

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

* fix(docs): sync Quiver block color card to white chip

Reflects the Quiver bgColor change (#000000 -> #FFFFFF) in the docs block info card.

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

* improvement(icons): enlarge AWS/Cloudflare/Dagster icons, fully white Zoom

- Enlarged (tighter viewBox, render-verified, no clipping): Cloudflare, Dagster,
  and the red AWS icons AWS IAM, Identity Center, Secrets Manager, SES, STS.
  Identity Center was anomalously small (filled ~32% of its frame); the group is
  now sized consistently (~80% fill).
- Zoom: the camera lens triangle was still #0B5CFF (blue-on-blue); switched it to
  currentColor so the whole camera renders white on the blue chip.

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

* docs(wiza): consolidate individual reveal into a single operation

Merges the separate Start/Get Individual Reveal operations into one Individual
Reveal operation in the Wiza docs and integrations data (operationCount 5 -> 4).

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

* improvement(icons): size remaining AWS icons to match the set (~80% fill)

Bring RDS, DynamoDB, SQS, CloudFormation, Athena, CloudWatch and S3 up to the
same ~80% fill as the AWS IAM/Identity Center/Secrets Manager/SES/STS group, so
all AWS icons are visually consistent. Bedrock left as-is (already ~92% fill).

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

* fix(icons): use Bright Data flame mark, enlarge ZoomInfo

- Bright Data: the full 'bright data' wordmark was illegible at chip size.
  Replaced with just the flame-'i' brand mark (blue #4280f6 on the white chip),
  centered.
- ZoomInfo: cropped the viewBox toward the white 'Zi' so it's larger; the red
  rounded-square background still fills the chip.

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

* improvement(icons): enlarge CrowdStrike icon

The falcon mark sat small in its chip because the icon used a wide 768x500
viewBox (letterboxed in the square chip). Switched to a square viewBox centered
on the mark so it fills ~80%, consistent with the other icons.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:44:05 -07:00
Waleed e1e773f487 feat(slack): add install + privacy section to integration landing page (#4799)
* feat(slack): add install + privacy section to integration landing page

Adds a hand-authored, slug-keyed landing-content module (separate from the generated integrations.json so it survives regeneration) and renders an install walkthrough + privacy-policy link on integration pages when present. Also refreshes generated docs (data-enrichment entry, icon mappings, tool mdx).

* fix(landing): render privacy section independently, align CTA analytics label

* docs(landing): clarify the Slack install button is behind sign-in

* refactor(landing): bake integration landing content into generated json via docs-gen

Moves landing content (install walkthrough + privacy) out of a render-time augment and into the generation pipeline: generate-docs reads the pure-data content map and writes landingContent into integrations.json, so the page reads a single source (integration.landingContent). Canonical types live in integrations/data/types.ts.
2026-05-29 16:33:30 -07:00
Vikhyath Mondreti 4b0dab4682 chore(copilot): deprecate mcp server (#4797)
* chore(copilot): deprecate mcp

* update error codes

* deprecate copilot api v1 route
2026-05-29 14:34:15 -07:00
Theodore LiandClaude Opus 4.7 7f24ae12fd feat(block): Add data enrichment block (#4774)
* feat(enrichment): workflow Enrichment block + /api/tools/enrichment/run

Add a generic Enrichment workflow block that runs a code-defined enrichment
(Work Email, Phone Number, Company Domain, Company Info, …) and returns its
outputs — usable in workflows, not just tables.

- New internal endpoint POST /api/tools/enrichment/run (checkInternalAuth +
  contract) runs the same runEnrichment provider cascade; injects the
  workspace's hosted/BYOK key via executeTool.
- New tool enrichment_run posts to it and surfaces hosted-key cost on the
  output so the workflow logging session bills it.
- New block blocks/blocks/enrichment.ts generated from the enrichment registry:
  operation dropdown = enrichments, per-enrichment conditional inputs, union of
  conditional outputs. New registry entries appear automatically.
- EnrichmentRunContext.tableId/rowId made optional (workflow path has no row).
- Register tool + block; bump api-validation route baseline; add EnrichmentIcon
  and generated docs page.

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

* chore: re-trigger CI

* refactor(enrichment): share mapFieldType helper between block and tool

* fix(enrichment): reserved output keys (matched/provider) win over enrichment outputs

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 22:19:08 -04:00
Waleed f2c17d8072 feat(integrations): add RB2B integration (#4784)
* make zoominfo icon a bit bigger

* feat(integrations): add RB2B integration

Add the RB2B AI workspace integration for person-level visitor identification and B2B enrichment via the RB2B API (api.rb2b.com).

- 15 tools: credit check, IP to HEM/MAID/company, email/HEM to business profile/best LinkedIn/LinkedIn slug/MAID, email to last active date, and LinkedIn to business profile/best personal email/personal email/hashed emails/mobile phone, plus LinkedIn slug search
- Single API-key block with an operation dropdown, conditional inputs, and outputs covering every tool
- RB2B icon and generated docs

* fix(integrations): address RB2B PR review

- email_to_activity now has its own plaintext Email field (canonicalParamId) instead of sharing the Email-or-MD5 input, since it does not accept MD5 hashes
- RB2BIcon uses useId() for its clipPath id instead of a hardcoded id, matching the convention used by other icons

* fix(integrations): map RB2B email_to_activity input via params, not canonicalParamId

The canonical-pair validation test requires canonical members to share the same
condition. Replace the cross-operation canonicalParamId reuse with two distinct
subblock ids (email for HEM ops, emailAddress for email_to_activity) and a small
tools.config.params remap to the email tool param.
2026-05-28 18:04:55 -07:00
WaleedandClaude Opus 4.8 c898e2e623 feat(integrations): add ZoomInfo, align Wiza, audit Apollo, refresh docs (#4776)
* feat(integrations): add ZoomInfo, align Wiza, audit Apollo, refresh docs

- Add ZoomInfo integration: search/enrich contacts & companies, intent, news (6 tools), proxy route, block, and icon
- Validate and align Wiza tools/block/outputs against live API docs
- Audit Apollo tools: tighten params, outputs, and types
- Update tool docs (.mdx), icons, icon mappings, and integrations.json

* fix(zoominfo): use useId for ZoomInfoIcon clipPath to avoid duplicate DOM ids

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

* fix(apollo): address PR review on sequence add and bulk enrich

- sequence_add_contacts: send large contact_ids/label_names arrays in the
  POST body (Rails merges query + body params) to avoid reverse-proxy URL
  length limits; keep scalar settings in the query string
- organization_bulk_enrich: add back-compat shim mapping the legacy
  `organizations` ({name, domain}[]) subBlock value to the new `domains`
  string array so saved workflows keep running

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

* fix(integrations): unique ZoomInfo icon clip id, numeric employee range filters

- ZoomInfoIcon: derive clipPath id from useId() so multiple instances don't collide
- ZoomInfo company search: send employeeRangeMin/Max as numbers, matching revenueMin/Max

* fix(zoominfo): send employeeRange filters as strings per API schema

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

* fix(zoominfo): send contactAccuracyScoreMin as string per Contacts Search schema

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

* fix(apollo): harden people_search pagination, correct bulk-update output docs

- people_search: read pagination from both the nested `pagination` object
  (legacy /mixed_people/search) and top-level fields, avoiding silent
  fallback to defaults
- account_bulk_update: correct output descriptions — accounts support up to
  1000 per request and async is opt-in (not auto-triggered at 100 like contacts)

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

* fix(wiza): correct company enrichment credits shape in output docs

Company enrichment returns api_credits { total, company_credits }, not the email/phone/scrape breakdown used by individual reveals. Description-only fix verified against docs.wiza.co.

* fix(apollo): send sequence add contact_ids/label_names as query params per docs

Apollo documents every field for emailer_campaigns/:id/add_contact_ids as a query parameter with no request body. Append contact_ids[]/label_names[] to the query string instead of the JSON body to match the documented contract.

* feat(apollo): expose account_stage_id uniform field for bulk update accounts

Apollo documents account_stage_id as a Body Param for /accounts/bulk_update ('when using account_ids, apply this account stage to all accounts'). Adds it to the tool params, body builder, type, block subBlock, and params mapper alongside name/owner_id.

* docs(apollo): correct contact_update typed_custom_fields description

Apollo's update-a-contact endpoint documents typed_custom_fields, so drop
the inaccurate "not officially documented" caveat.

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

* fix(zoominfo): default required outputFields on enrich; parse nested API error object

- ZoomInfo enrich endpoints require outputFields; send a curated default set when omitted so requests don't fail
- extractZoomInfoError now reads the GTM REST nested error object ({error:{code,message}}) instead of dropping it to a generic HTTP message

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

* improvement(wiza): add wandConfig to complex prospect-search filter fields

Adds AI-assist wandConfig (json-object) with format examples to the structured filter inputs (job_title, job_company, past_company, company_industry, location, company_location) and the full filters object, completing the wandConfig checklist item for the Wiza block.

* fix(findymail): surface API .error messages and alphabetize registry

- transformResponse error branches now fall back to the response body's
  `error` field before the generic status string, so Findymail's actual
  messages ("Not enough credits" on 402, "Subscription is paused" on 423,
  "One identifier is required..." on 422) reach the user instead of a
  bare "Findymail API error: <status>". Applied to all 11 tools.
- alphabetize the findymail entries in tools/registry.ts to match the
  already-alphabetical import block and the integration guideline.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-28 15:30:25 -07:00
Vikhyath Mondreti 28766ddaa9 feat(instantly): block, trigger (#4763)
* feat(instantly): block, trigger

* change bg color for icon

* address comments

* cleanup code

* search param missplaced
2026-05-27 16:36:21 -07:00
Vikhyath Mondreti 4fab03c595 improvement(media-gen): retire vision block, add hosted key for fal ai for image/video gen, search visibility in cmd-k (#4684)
* improvement(media-gen): retire vision block, add hosted key for fal ai for image/video gen

* address comments
2026-05-23 12:54:14 -07:00
Waleed 952eb1216f feat(mailer): add AWS SES and SMTP providers with auto-detect fallback (#4710)
* feat(mailer): add AWS SES and SMTP providers with auto-detect fallback

* fix(mailer): cast SES options to bridge duplicate @aws-sdk type identities

* fix(mailer): dedupe aws-sdk-sesv2, address review feedback

- Force a single @aws-sdk/client-sesv2 install via root package.json overrides; @types/nodemailer pulled in a nested copy whose nominal class brand made the two SDK type identities incompatible, breaking the CI build. With one install the cast disappears.
- Batch result message now reports successCount instead of sendable.length when entries are skipped, so "5 emails sent" no longer overstates delivery on partial failures.
- SMTP provider now warns when SMTP_HOST is set without SMTP_PORT, and when only one of SMTP_USER/SMTP_PASS is set — both previously silent misconfigurations.
- SMTP_SECURE schema is z.boolean() to match every other boolean in env.ts; runtime parsing is still handled by envBoolean.
- Strip the verbose TSDoc comments I had added.

* fix(mailer): exact sent counts in batch results, restore SES type cast

- mergeBatchResults: data.count and the message now report only emails that were actually delivered, not skipped-unsubscribed ones (they returned success: true and inflated the count). Empty-sendable branch distinguishes "all unsubscribed" from "mixed skip/failure" so the message stops lying when some entries fail validation.
- ses.ts: revert the package.json override approach (bun honors it locally but CI still installs a nested @types/nodemailer copy). Reinstate the `as unknown as` cast with a single-line WHY comment.

* fix(mailer): annotate double-cast in ses provider for strict api-validation

* fix(mailer): batch degrades isUnsubscribed errors to per-entry failures

A transient DB error in isUnsubscribed used to abort the whole batch
because the call sat outside the per-email try/catch in prepareBatch.
Wrap the unsubscribe check inside the same catch so a rejection becomes
a per-recipient failure, matching sendEmail's behavior. Lock it in
with a regression test.
2026-05-21 19:27:33 -07:00
Waleed 21c956cf97 improvement(hubspot): OAuth-native polling trigger replacing webhook flow (#4705)
* improvement(hubspot): OAuth-native polling trigger replacing webhook flow

* feat(hubspot): property autocomplete, multi-filter, property-changed, list-membership, pipeline/owner dropdowns

* fix(hubspot): freeze cursor on failure + request full OAuth scopes

* chore(api): bump API route baseline from 749 to 753 for HubSpot selector routes

* fix(hubspot): make eventType required conditional on visibility

* improvement(hubspot): align trigger name and longDescription with poll-trigger conventions

* fix(hubspot): encodeURIComponent on search path segment for defense-in-depth

* fix(hubspot): cursor-based seed for list_membership polling

* fix(hubspot): Map-backed property snapshot + drop redundant filter parse
2026-05-21 17:25:19 -07:00
Vikhyath Mondreti 974a18dd61 improvement(media-blocks): new versions of image and video gen with latest models + fixes (#4667)
* improvement(media-blocks): new versions of image and video gen with latest models + fixes

* respect versioning for icons

* fix integration routes

* address comments

* address api mismatches

* more ltx 2.3 durations

* typing tightness
2026-05-19 16:42:04 -07:00
Waleed 6c755cbb59 feat(integrations): add Gong incident.io Railway and New Relic (#4663)
* feat(integrations): add Gong incident.io Railway and New Relic

* fix(railway): preserve explicit empty variable values

* fix(incidentio): fail on invalid workflow JSON

* fix(new-relic): validate custom attributes JSON

* fix(integrations): address incident workflow review fixes

* chore(docs): apply lint formatting

* chore: refresh integration docs and validation fixes

* more

* fix(integrations): address PR review comments

* fix(gong): align list calls block validation
2026-05-19 14:12:28 -07:00
df1e2ddc2e feat(azure-devops): block and trigger (#4664)
* azure devops logo on white

* generated ADO tool docs

* generated ADO tool docs

* added ADO to registries

* ADO workflow triggers

* ADO workflow triggers

* tool layer for ADO, checks passed and manual verified

* ADO workflow triggers

* block layer for ADO

* ADO icon svg

* generated docs for ADO triggers

* committing the tests for azure devops tools and blocks

* Update apps/sim/triggers/azure_devops/utils.ts

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

* Update apps/sim/tools/azure_devops/update_work_item.ts

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

* Update apps/sim/triggers/azure_devops/utils.ts

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

* comma syntax error patched

* azure devops: validate-integration fixes + manual description

- bgColor switched from white to Azure DevOps brand color #0078D4 (block + mdx)
- WIQL query_work_items: hydrate ALL matched IDs by chunking through batches
  of 200 instead of silently truncating; check response.ok on the follow-up
  fetch and surface a clear error on 4xx/5xx; trim org/project; expose
  totalMatched in metadata so users can see pre-hydration count
- Add MANUAL-CONTENT-START:intro section to the azure_devops.mdx docs page
- Update unit tests for new chunking behavior and update-work-item validation

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

* azure_devops: second-pass audit fixes + formatter cleanup

- Add types barrel export to tools/azure_devops/index.ts
- Normalize comment endpoint path casing (/workItems/ -> /workitems/)
- Update test assertions to match normalized path
- Biome formatter reflow across tools, triggers, registry, and docs icon

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

* azure_devops: address PR review comments

- Fix bgColor #FFFFFF -> #0078D4 in integrations.json and triggers/azure_devops.mdx
- Bump File tool operationCount from 4 to 5 (Read, Fetch, Get, Write, Append)
- Apply .trim() to org/project across all 15 remaining tools (consistency with query_work_items)
- Fix Found ${data.count} -> Found ${data.count ?? items.length} fallback in list_builds, list_pipelines, list_pipeline_runs content strings

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

* idemtpotency

* azure_devops: address bugbot review comments

- triggers/utils: match build.complete result case-insensitively, accept stopped/cancelled in addition to failed/canceled/partiallySucceeded so PascalCase and legacy Azure DevOps payloads aren't dropped
- get_work_items_batch: chunk comma-separated IDs into 200-batch loops with proper status checks (was failing or returning incomplete data on >200 IDs)
- Add tests for both behaviors

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

* azure_devops: address additional bugbot comments

- Block update_work_item now forwards areaPath; the Area Path subblock condition expanded to include update operation
- get_build_timeline.failedRecords now also flags partiallySucceeded and succeededWithIssues, normalized case-insensitively. Output description and added a focused test

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

* azure_devops: address more bugbot comments

- Webhook provider extractIdempotencyId returns null when subscriptionId or notificationId is missing/empty, preventing the literal "azure_devops:undefined:undefined" key from collapsing unrelated deliveries into duplicates
- Get Work Items Batch validates that at least one non-empty ID is supplied before issuing the API request, throwing a clear error instead of hitting an empty ids= query
- Tests cover both behaviors

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

* azure_devops: pin add_comment to documented api-version 7.0-preview.3

Microsoft's Add Comments docs only publish 7.0-preview.3 (the 7.2 view falls back to the 7.0 page). Get Comments stays on the documented 7.2-preview.4. Matches what's strictly in the Azure DevOps REST API reference rather than relying on undocumented version behavior.

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

---------

Co-authored-by: Marcus Chandra <mzxchandra@gmail.com>
Co-authored-by: mzxchandra <129460234+mzxchandra@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 14:10:42 -07:00
Waleed b98164fd3d feat(wiza): add Wiza integration for B2B prospect enrichment and search (#4662)
* feat(wiza): add Wiza integration for B2B prospect enrichment and search

* fix(wiza): coerce reveal id to string, skip empty filters in prospect search

* fix(wiza): throw on invalid JSON in advanced filter fields instead of silently dropping
2026-05-19 12:06:19 -07:00
Vikhyath Mondreti 279010a2e7 improvement(execution): memory usage for aggregated results (#4650)
* improvement(execution): memory usage for aggregated results

* progress

* address comments

* loop/parallel results compaction

* address comment

* remove build files, harden edge cases

* remove hotpath serialiazation

* display change to make use of only preview

* materialize refs before sending in response block

* preserve exact large-value access through workflow materialization

* address comments

* progress

* fix notif error + sync manifest undefined exit

* fix streaming ref materialization

* fix tests
2026-05-18 23:55:06 -07:00
Waleed 0c1167d76b improvement(workspace): fix resource table column proportions and toast stacking (#4655)
* improvement(workspace): fix resource table column proportions and toast stacking

* fix(resource): restore scrollbar-gutter stable on table scroll container

* fix(resource): remove scrollbar-gutter stable — single-table layout doesn't need it

* fixed files cols

* fix(findymail): add required enabled field to wandConfig entries

* fix(findymail): remove optional from block outputs — not valid on BlockConfig output type

* fixes

* fix(files): use folderSizeMap for sort value so size sort matches display

* files ref
2026-05-18 17:49:51 -07:00
Waleed f3cf8fc55d feat(findymail): add Findymail B2B contact data integration (#4654)
* feat(findymail): add Findymail B2B contact data integration

Adds 11 tools covering verified email lookup (by name, LinkedIn, domain
roles), email verification, reverse email lookup with profile enrichment,
company info, employee discovery, phone lookup, technology stack
detection, and credit checks. Single API-key block with operation
dropdown, gradient-rendered icon, and generated docs.

* fix(findymail): handle HTTP errors and surface last_detected_at

- All 11 tools now check response.ok and return success:false with the API error message on non-2xx responses
- search_technologies now maps last_detected_at to match lookup_technologies and the shared output schema
- Restore file_v3 in docs icon-mapping (translated docs still reference it)

* improvement(findymail): exclude operation from params transform

Match the convention used by enrich/apify/box/calendly — destructure out
operation before forwarding the rest to the tool call, so the operation
key doesn't leak into the tool payload.
2026-05-18 12:53:32 -07:00
Waleed b276672867 feat(prospeo): add Prospeo integration for B2B contact enrichment and search (#4653)
* feat(prospeo): add Prospeo integration for B2B contact enrichment and search

Adds 8 operations: enrich person/company, bulk enrich person/company,
search person/company, search suggestions, and account information.
Uses X-KEY header auth.

* refactor(prospeo): extract shared parse helpers into utils.ts
2026-05-18 12:08:00 -07:00
Theodore Li fffb87901d feat(wait): Async toggle, chained-wait resume fix, execution status API (#4514)
* fix(wait): poll partially_resumed rows so chained waits resume

The chained-pause flow leaves a row in 'partially_resumed' status (wait1 done, wait2 still waiting). The poll's WHERE filter only matched 'paused', so wait2 was never picked up. Include 'partially_resumed' in the filter.

* feat(wait): make in-process threshold env-overridable for local testing

Adds WAIT_INPROCESS_MAX_MS env var (default 300000ms = 5 min). Lower it locally (e.g. 5000) to exercise the suspend/cron-resume path with short waits.

* feat(wait): add Suspend Workflow toggle, restore 5-min in-process default

* fix(wait): address bot review — setNextResumeAt + suspend unit default

- setNextResumeAt now matches paused OR partially_resumed; otherwise the
  cron poller can't null nextResumeAt after dispatching a chained-wait
  row, so it keeps reappearing in every poll batch until execution ends
  (flagged by both greptile and bugbot)
- Suspend mode now defaults missing timeUnitLong to 'minutes' instead of
  falling back to 'seconds' and immediately erroring (flagged by bugbot)

* improvement(wait): hint the wait-amount cap on the input

Restores the pre-#4331 description on the Wait Amount field so the limit is visible before submit instead of only at runtime. Mentions both the 5 min default and the 30 day cap with Suspend Workflow.

* refactor(wait): rename Suspend Workflow toggle to Async

* fix(wait): reword cap errors to say 'async mode'

* feat(workflows): add GET /workflows/[id]/executions/[executionId] status endpoint

Normalized status (pending|running|paused|completed|failed|cancelled)
across workflowExecutionLogs and pausedExecutions in a single response.
Surfaces paused-state details (resumeAt, pauseKind, blockedOnBlockId)
when a row exists in pausedExecutions, and the error string for failed
runs. finalOutput is opt-in via ?includeOutput=true.

* feat(workflows): support ?selectedOutputs= on execution status endpoint

Returns per-block outputs filtered by selectedOutputs paths (same
shape as the execute endpoint). Reads from executionData.traceSpans,
walks children recursively, and resolves dot-paths into each block's
output. Bare blockId returns the full output.

* fix(wait): drop dead tooltip prop on Async switch

Switch sub-blocks return null from renderLabel (sub-block.tsx:238), so
the tooltip never reached the user. The trade-off explanation already
lives in longDescription and bestPractices. Flagged by bugbot.

* docs(api): document GET /workflows/[id]/executions/[executionId]

Adds the WorkflowExecutionStatus schema and the getWorkflowExecution
operation to the OpenAPI spec, including completed/paused/failed
response examples and the includeOutput + selectedOutputs query params.
Registers the page in the Workflows section of the API reference.

* docs(api): tighten getWorkflowExecution descriptions
2026-05-15 22:09:06 -04:00
Waleed 11fa96cac0 chore(deps): bump next to 16.2.5 for CVE-2026-44578 SSRF fix (#4606)
* chore(deps): bump next to 16.2.5 for CVE-2026-44578 SSRF fix

* chore(deps): bump next to 16.2.6 for full May 2026 security release coverage
2026-05-14 14:34:05 -07:00
WaleedandClaude Opus 4.7 c21bb915f7 improvement(grafana): align tools and block with Grafana API spec (#4574)
* improvement(grafana): align tools and block with official Grafana API spec

Validates and corrects the Grafana integration against the official API
docs: fixes wire-format field naming for provisioned alert rules
(missing_series_evals_to_resolve, keepFiringFor, orgID), adds
X-Disable-Provenance support, expands alert-rule params (isPaused,
notificationSettings, record, annotations, labels), corrects defaults
(execErrState=Error, dashboard overwrite=false), and centralizes alert-rule
output mapping in a shared utils module.

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

* fix(grafana): correct wire-format casing for provisioned alert rule fields

Grafana's ProvisionedAlertRule schema (verified against upstream Go source
and swagger spec) uses keep_firing_for (snake_case) and
missingSeriesEvalsToResolve (camelCase) — the opposite of what prior audit
rounds assumed. POST/PUT bodies now send the correct field names; mapAlertRule
reads the correct primary names with the old casings kept as fallbacks.

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

* fix(grafana): address PR review feedback

- Drop hardcoded orgID: 1 fallback; only send orgID when organizationId is
  provided, so token-scoped org context drives rule placement.
- Surface invalid JSON for notificationSettings/record on alert rule
  create/update instead of silently dropping the input.
- Fix execErrState description in update_alert_rule to include Error.

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

* fix(grafana): surface invalid JSON for annotations/labels/data on alert rules

Match the behavior of other JSON params (data, notificationSettings, record):
return a descriptive error instead of silently falling back to {} (create)
or keeping the existing value (update).

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

* docs(grafana): expose alert-rule output fields in generated docs

Move ALERT_RULE_OUTPUT_FIELDS from utils.ts to types.ts and rename to
SCREAMING_SNAKE_CASE so scripts/generate-docs.ts (which only resolves const
references from types.ts matching [A-Z][A-Z_0-9]+) can inline the per-field
rows into the generated alert-rule output tables.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 19:42:20 -07:00
Vikhyath Mondreti 43f53bb7d3 feat(execution): payload size bottlenecks with lazy execution value hydration, safer materialization, and batched parallel execution (#4560)
* improvement(resolver): lazy resolution for underlying fields greater than 10MB

* progress

* feat(parallel): batching

* codegen to allow inline substitution

* address comments

* ui inconsistencies

* cleanup redundant code

* address more comments

* address comments

* replace helper

* fix tests
2026-05-12 14:57:32 -07:00
Waleed f6b246ba44 fix(docs): restore media centering and full-width intro image (#4570)
* fix(docs): restore media centering and full-width intro image

* fix(docs): drop overflow-hidden from intro media wrappers so focus ring is not clipped

* fix(docs): use inset focus ring on lightbox media so parent overflow-hidden cannot clip it

* fix(docs): drop focus ring on lightbox media to match original UI
2026-05-12 14:56:19 -07:00
Waleed 39f74aa353 feat(data-drains): add GCS, Azure Blob, BigQuery, Snowflake, and Datadog destinations (#4552)
* feat(data-drains): add GCS, Azure Blob, BigQuery, Snowflake, and Datadog destinations

* fix(data-drains): address PR review comments

* fix(data-drains): extract sleepUntilAborted, honor abort across all destinations

* fix(data-drains): widen BigQuery projectId max and dedupe parseServiceAccount

* fix(data-drains): tighten GCS bucket contract and expose Azure endpointSuffix

* improvement(data-drains): extract normalizePrefix and buildObjectKey to shared utils

* fix(data-drains): retry BigQuery network errors; tighten Azure accountKey contract

- BigQuery insertAll now wraps the fetch in try/catch inside the retry loop so DNS failures, socket resets, and timeouts are retried with backoff instead of propagating immediately.
- Align azureBlobCredentialsBodySchema with the runtime schema (min 64 / max 120 / base64 regex) so obviously invalid keys are rejected at the API boundary rather than at drain-run time.

* improvement(data-drains): consolidate parseRetryAfter; add Datadog NDJSON line context

- Extract a single parseRetryAfter helper (capped at 30s, returns number | null) into lib/data-drains/destinations/utils.ts and remove the five local copies in bigquery, datadog, gcs, snowflake, and webhook.
- Datadog parseNdjson now wraps JSON.parse in try/catch and surfaces the failing line index, matching BigQuery's parser.

* fix(data-drains): correct Datadog size guard and Snowflake VARIANT limit

- Datadog payload guard now checks the uncompressed size against the 5 MB limit and the wire size against the 6 MB compressed limit, so gzip cannot smuggle an oversized body past the client-side check.
- Snowflake VARIANT limit is 16 MiB (16,777,216 bytes), not 16,000,000 bytes — small payloads between 16 MB and 16 MiB were being rejected unnecessarily.
- Drop the unused apiKey field on Datadog PostInput; the key is already embedded in the prepared request headers.

* improvement(data-drains): consolidate backoffWithJitter into shared utils

Datadog, GCS, and webhook each had byte-identical backoff helpers (BASE 500ms, MAX 30s, jitter ±20%, Retry-After floor). Lift the helper into lib/data-drains/destinations/utils.ts alongside parseRetryAfter and sleepUntilAborted, and drop the per-file copies and their BASE_BACKOFF_MS/MAX_BACKOFF_MS constants.

* fix(data-drains): align destinations with live provider specs

Audited every destination against live AWS/GCS/Azure/BigQuery/Snowflake/
Datadog/webhook docs and applied spec-correctness fixes:

- S3: reserved bucket prefix amzn-s3-demo-, suffixes --x-s3/--table-s3;
  metadata byte formula excludes x-amz-meta- prefix per AWS spec
- GCS: reject -./.- adjacency; UTF-8 prefix cap; forbid .well-known/
  acme-challenge/ prefix; ASCII-only x-goog-meta-* enforcement
- BigQuery: insertId is 128 chars (not bytes); split DATASET_RE (ASCII)
  and TABLE_RE (Unicode L/M/N + connectors); UTF-8 byte cap on tableId
- Snowflake: disambiguate org-account vs legacy locator account formats;
  requestId+retry=true for idempotent retries; server-side timeout=600;
  default column DATA uppercase to match unquoted canonical form
- Azure: endpoint suffix allowlist (4 sovereign clouds); accountKey
  length(88) base64
- Webhook: url max(2048); CRLF/NUL rejection on bearer/secret/sig header

* fix(data-drains): address PR review on snowflake poll + shared NDJSON parsing

- snowflake pollStatement: per-attempt timeout via AbortSignal.any, retry on 429/5xx with Retry-After + jitter
- bigquery parseNdjson error messages now 1-indexed
- consolidate parseNdjson variants into shared parseNdjsonLines/parseNdjsonObjects in utils

* fix(data-drains): per-attempt fetch timeouts in gcs/bigquery, snowflake poll double-sleep

- gcs.fetchWithRetry + bigquery.postInsertAll now use AbortSignal.any with a per-attempt timeout so a hung TCP connection cannot stall the drain
- snowflake.pollStatement skips the next interval sleep when it just slept for retry backoff

* fix(data-drains): bigquery probe timeout + jittered retries, align Snowflake column default UI/docs

- bigquery test() probe now uses AbortSignal.any + per-attempt timeout
- bigquery insertAll retry switches to backoffWithJitter for thundering-herd avoidance
- Snowflake column placeholder + docs say DATA (uppercase) to match the code default

* fix(data-drains): mirror webhook signingSecret min length in form gate

isComplete now requires signingSecret >= 32 to match the contract/runtime
schema so the Save button can't enable on a value that will fail server-side.

* fix(data-drains): validate JSON client-side for Snowflake before binding

Switch Snowflake to parseNdjsonObjects so malformed rows are caught locally
with 1-indexed line numbers instead of failing the whole INSERT server-side.
Re-stringify each parsed object before binding to PARSE_JSON(?).
Drop the now-unused parseNdjsonLines helper.

* fix(data-drains): cross-cutting audit pass against live provider docs

- Azure: bound retryOptions on BlobServiceClient (SDK default tryTimeoutInMs is per-try unbounded; cap at 30s x 5 tries)
- Webhook contract: mirror runtime — signingSecret.max(512), bearerToken.max(4096) + CRLF/NUL refine, signatureHeader charset + CRLF/NUL refine
- S3 (lib + contract): reject bucket names with dash adjacent to dot; require https:// endpoint at the schema layer
- Snowflake: bind original NDJSON line bytes (re-stringifying a JSON.parse'd value loses bigint precision beyond 2^53-1); check pollStatement 200 body for the SQL error envelope (sqlState/code)
- Datadog: entry builder writes defaults first then user attrs then forced ddtags/message so user rows can't clobber routing fields; validate config.tags as comma-separated key:value pairs
- registry.tsx: tighten isComplete predicates to mirror contract minimums (GCS bucket >= 3, Azure containerName >= 3 / accountKey === 88, BigQuery projectId >= 6, Snowflake account >= 3)

* fix(data-drains): force ddsource/service overrides on Datadog entries

Previous fix placed ddsource/service before ...attrs, leaving them clobberable
by a user row field. Per Datadog docs, service + ddsource pick the processing
pipeline, so a drain's routing config must not be overridable per-row. Spread
attrs first, then force all four reserved fields (ddsource, service, ddtags,
message).

* fix(data-drains): preserve row-distinguishing index when BigQuery insertId overflows

Truncating from the left dropped the index suffix, so any overflow would
collapse all rows in a chunk to the same insertId and BigQuery would silently
dedupe them. Path is unreachable today (UUIDs keep raw ~85 chars), but the
overflow branch is now correct: hash the prefix, keep the index intact.

* fix(data-drains): refresh GCS token per retry, tighten Azure key regex

- gcs: rebuild Authorization header per attempt via buildHeaders so token
  refresh from google-auth-library kicks in if a 5xx retry crosses the
  hour-long token lifetime
- azure_blob: pin account-key regex to {0,2} trailing '=' (base64 of 64
  bytes = exactly 88 chars with up to two '=' pad chars)

* fix(data-drains): address bugbot review of 6336948f6

- gcs: allow 1-char dot-separated bucket components (e.g. "a.bucket")
  to match GCS naming rules — overall name is 3-63 (or up to 222 with
  dots), but per-component minimum is 1 per Google's spec
- bigquery: drain the 401 response body before re-issuing the request
  with a refreshed token so undici can return the socket to the
  keep-alive pool
- snowflake: hoist getJwt() above the perAttempt timer in
  executeStatement so JWT signing doesn't eat the network budget
  (matches the order already used in pollStatement)

* fix(data-drains): allow org-account Snowflake identifier with region suffix

The account validation rejected `<orgname>-<acctname>.<region>.<cloud>`
because `ACCOUNT_LOCATOR_RE`'s first segment forbade hyphens, while
`ACCOUNT_ORG_RE` forbade dots. `normalizeAccountForJwt` already handles
this composite form. Widen the first segment of `ACCOUNT_LOCATOR_RE` to
allow hyphens so the boundary contract and the runtime schema accept
what the JWT layer was already designed to process.

* fix(data-drains): drain retryable response bodies in datadog/gcs loops

Mirrors the bigquery 401 fix. Without consuming the body before
sleeping, undici can't return the socket to the keep-alive pool, so
each retry leaks a TCP connection instead of reusing it.

* fix(data-drains): drain snowflake poll bodies on 202 and retryable status

Mirrors the bigquery/datadog/gcs drains. Long async statements can poll
many times against the same connection; without consuming the body
undici can't return the socket to the keep-alive pool, so each iteration
leaks a connection until GC.

* fix(data-drains): consume success bodies; check Snowflake sqlState on 200

- gcs: drain the body on success paths so undici can return the socket
  to the keep-alive pool
- snowflake: drain the body on synchronous 200 OK and run the same
  sqlState envelope check pollStatement already does — otherwise a
  statement-level failure that completes synchronously would silently
  return success

* fix(data-drains): drain datadog and bigquery probe success bodies

Same undici keep-alive issue as the prior fixes: postWithRetries
returned the Response on success without draining (callers only read
headers); the BigQuery `test()` probe returned without consuming the
body. Both now drain before returning.

* chore(data-drains): regenerate enum migration as 0206 after staging rebase

* fix(data-drains): cap snowflake poll retries; tighten datadog tags min length
2026-05-11 17:37:10 -07:00
Waleed ae680afab2 improvement(data-drains): docs page, screenshots, and search/UI polish (#4547)
Adds the enterprise data-drains docs page with two screenshots, a
search bar over the drains table, and a UI cleanup pass on the
settings component (size-*, useMemo removal, text-sm fixes).

The previously-proposed env-var reference feature has been dropped —
drain credentials remain raw values, encrypted at rest by the
existing pipeline.
2026-05-10 13:43:25 -07:00
WaleedandCursor 3ed8615b5d chore(cleanup): react-doctor dead code elimination, landing + docs overhaul, component modernization (#4544)
* fix(react-doctor): remove unused export types, useEffect clearTimeout missing, a11y fixes

* fix(react-doctor): strip unused export types from contracts, copilot, stores, and components

Remove export keyword from type/interface declarations confirmed to have zero importers
across lib/api/contracts/tools/aws/, lib/api/contracts/*.ts, lib/copilot/generated/,
stores/workflows/workflow/types.ts, ee/access-control, ee/data-retention, lib/logs/types.ts,
and app/workspace component files. TypeScript and API validation both pass clean.

Reduces unused-types count from 394 → 181 and fully eliminates the ✗ critical
dead-code categories (exports, types, files now show as ⚠ warnings not ✗ errors).

* docs improvements

* fix(react-doctor): delete 50 unused files (dead barrels, unreachable components, stale utilities)

Remove confirmed-unused barrel index.ts files across stores/, connectors/, executor/,
lib/, and app/workspace/ that had zero importers. Also delete unreachable components
(chat-history-skeleton, trace-spans, logs-list, template-profile, enterprise landing
sections), stale utilities (buffered-stream, blob-to-data-url, queued-workflow-execution,
compute-edit-sequence), and obsolete generated/contract files. TypeScript passes clean.

* remove dead code

* cleanup

* more

* fix(blog): restore DiffControlsDemo for v0-5 blog post

* fix: restore ContactButton for enterprise blog post, export WIKIPEDIA_PAGE_CONTENT_OUTPUT_PROPERTIES

* fix(react-doc): restore stripped exports and remove server-only dependency

* chore(deps): update lockfile after removing server-only

* added back some exports

* docs

* more

* fix type issues

* tc

* fix docs search route

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(tag-dropdown): add missing isEqual import from es-toolkit

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-10 12:00:05 -07:00