Commit Graph
5193 Commits
Author SHA1 Message Date
Waleed a08da86dff feat(wordpress): add category/tag CRUD tools, fix delete-status and dead-field bugs (#5360)
* feat(wordpress): add category/tag CRUD tools, fix delete-status and dead-field bugs

- Add wordpress_{get,update,delete}_category and _tag tools for full taxonomy parity
- Fix `deleted: data.deleted || true` always evaluating true across all 6 delete tools (posts/pages/media/comments/categories/tags)
- Remove dead `force` param from delete_media (endpoint always force-deletes; param had zero effect)
- Remove unwired `hideEmpty` block input
- Normalize search_content perPage/page visibility to user-or-llm for consistency

* fix(wordpress): use ?? instead of || for zero-valued numeric fields in delete_category/tag

count and parent can legitimately be 0 (empty term, top-level category); || was
dropping those values, same antipattern already fixed for `deleted` in this PR.
Flagged independently by Greptile and Cursor Bugbot.

* fix(wordpress): use ?? for zero-valued numeric fields across all delete tools

Same || antipattern already fixed for category/tag delete tools was still
present in delete_post/page/comment/media for id, author, featured_media,
menu_order, parent, post — all legitimately 0 in common cases (no featured
image, top-level page/comment). Found by a final independent validation pass.

* fix(wordpress): don't drop categoryParent=0 (root-level category) in block param mapping

Truthy check on params.categoryParent treated a resolved numeric 0 (root-level,
no parent) as unset. Flagged by Cursor Bugbot on create_category/update_category.

* fix(wordpress): don't clear category/tag description on update when field left blank

description used !== undefined (numeric-field convention) instead of a truthy
check (string-field convention used everywhere else in this codebase, e.g.
update_post/update_page excerpt), so an untouched empty description field
silently wiped existing text on every update. Flagged by Cursor Bugbot.

* fix(wordpress): fix search type/subtype mislabeling, complete I/O exposure gaps

- search_content.ts: type param was mislabeled with subtype's vocabulary
  (post/page/attachment); real WP type enum is post/term/post-format. Rewired
  the block's Content Type dropdown to map to subtype (which is what
  post/page/attachment actually filter), not type.
- Widened subBlock conditions so params already read by tools.config.params
  are actually reachable in the UI: commentPostId for list_comments,
  categories/tags for list_posts, parent for list_pages.
- Added missing subBlocks for tool params with no UI path: comment
  parent/authorName/authorEmail/authorUrl (create_comment), media description
  (upload_media), author filter (list_posts).
- Extended the ?? / !== undefined fix (already applied to categoryParent) to
  the same class of param across the block: featuredMedia, page parent,
  menuOrder, and the new commentParent/listAuthor mappings.
- Fixed featuredMedia/parent truthy-check inconsistency in create_post,
  update_post, create_page, update_page, create_category, create_comment
  body builders to match the !== undefined convention used elsewhere.

Found by an independent final validation pass across 3 parallel agents.

* fix(wordpress): keep searchType subBlock id to preserve saved-workflow compat

The type/subtype fix should only change which API param the field feeds
(subtype, not type) — renaming the subBlock id to searchSubtype broke
already-saved workflows with a search content-type filter set, since the
block would stop reading the old searchType key. Reverted the id rename,
kept the underlying subtype mapping fix. Flagged by Cursor Bugbot.

* fix(wordpress): remove invalid Attachment search subtype, fix listAuthor input type

- Search Content's "Content Type" dropdown offered Attachment, which maps to
  subtype=attachment. WP core's WP_REST_Post_Search_Handler explicitly
  excludes attachment from valid subtypes (media isn't searchable via
  /search) — selecting it guaranteed a 400 rest_invalid_param. Removed the
  option; only Post/Page (the only valid subtypes) remain.
- listAuthor was declared type: 'string' in the inputs catalog despite being
  Number()-coerced before use, inconsistent with every other ID-like field
  (postId, pageId, categoryId, commentParent, etc. are all 'number').

Found by an independent final pre-merge validation pass, requested before
merge to be certain of full API alignment.
2026-07-02 10:47:31 -07:00
Waleed 7a31871471 feat(clerk): expand Clerk integration with org, membership, moderation, and security tools (#5364)
* feat(clerk): expand Clerk integration with org, membership, moderation, and security tools

- fix 4 validate-integration warnings: missing .trim() on org/session IDs, incomplete session-status dropdown, missing list_users/list_organizations filter subBlocks
- add organization update/delete tools
- add organization membership CRUD (list, add, update role, remove)
- add organization invitation create/list
- add user ban/unban/lock/unlock and OAuth access token retrieval
- add allowlist/blocklist identifier management
- add JWT template list/get
- add actor token create/revoke (impersonation)
- add matching webhook triggers for session ended/removed/revoked, organization updated/deleted, and membership updated/deleted
- wire all 23 new tools into the block, tool registry, and trigger registry

* fix(clerk): I/O completeness fixes from final validation pass

- remove dead limit/offset params from list_blocklist_identifiers (Clerk API accepts zero params on this endpoint, verified across 6 spec versions)
- expose publicMetadata on OAuth access token output (was silently dropped)
- expose inviter email/first/last name (public_inviter_data) on organization invitation create/list outputs
- add missing orderBy param to list_organization_invitations
2026-07-02 10:47:08 -07:00
Waleed f33d325a88 fix(deps): bump echarts to 6.1.0 to patch XSS vulnerability (#5374)
Fixes GHSA-fgmj-fm8m-jvvx / CVE-2026-45249 — Lines series tooltip
rendering could execute raw HTML from series.data[i].name when no
custom tooltip.formatter is set.
2026-07-02 10:46:50 -07:00
Waleed 070f554047 feat(sharepoint): validate against Graph API and add delete/update/download tools (#5369)
* feat(sharepoint): validate against Graph API and add delete/update/download tools

- Fix bugs found validating existing tools against live Graph docs: malformed
  nested $expand syntax in get_list, reversed site-resolution precedence in
  create_list, unsanitized field fallback in add_list_items, missing URL
  encoding in read_page, and an incorrect root/serverRelativeUrl field shape
- Fix V1 block gaps vs V2: upload_file required flag, missing required on
  pageName/listDisplayName, wrong site-picker mimeType, dead subBlock refs
- Add 8 new tools within already-granted scopes for CRUD completion:
  delete_list_item, get_list_item, delete_page, update_page, publish_page,
  download_file, delete_file, get_drive_item
- Add opt-in stripAuthOnRedirect option to secureFetchWithPinnedIP so the
  SharePoint file-download route doesn't resend the bearer token to the
  preauthenticated redirect target (default off, no behavior change elsewhere)
- Dedupe read-only list-item field sanitization into a shared utils helper

* fix(sharepoint): encode siteId consistently and fix create_page precedence

- URL-encode siteId/groupId everywhere it's interpolated into a Graph
  request path (Graph site IDs like "host,guid,guid" contain commas that
  must be encoded) - addresses Cursor Bugbot findings on update_page,
  delete_page, publish_page, and applies the same fix consistently across
  every other sharepoint tool for consistency
- Fix create_page's site-resolution precedence (was siteSelector before
  siteId, inconsistent with every sibling tool)

* fix(sharepoint): escape HTML in page content and stop fallback from throwing

- create_page/update_page only escaped quotes when building innerHtml;
  add a shared escapeHtml() helper that also escapes &, <, > so page
  content with angle brackets/ampersands doesn't corrupt the canvas layout
- add_list_items' fields fallback (sanitized re-derivation when Graph's
  response omits fields) could throw on a malformed fallback input after
  the item was already created successfully; catch and fall back to
  undefined instead of failing an already-successful response

* fix(sharepoint): scope columnDefinitions->pageContent mapping to create_list

Both V1 and V2 tools.config.params mapped columnDefinitions onto pageContent
whenever columnDefinitions was truthy, with no operation check. Stale
list-column JSON left in block state after switching to create_page/
update_page would silently replace the user's intended page text. Gate
the mapping on operation === create_list (V1) / sharepoint_create_list (V2).

* fix(sharepoint): cap download-file content fetch at MAX_FILE_SIZE

Greptile flagged the content fetch as unbounded - a large file would
buffer entirely in memory before base64-encoding into the JSON response.
Pass maxResponseBytes: MAX_FILE_SIZE (100MB, same constant used by the
upload path) to secureFetchWithPinnedIP so oversized files reject early
with a clear PayloadSizeLimitError instead of exhausting memory.

* fix(sharepoint): close V1 block input/output gaps and encode upload URLs

Final validation pass (4 parallel audit agents against live Graph docs)
surfaced remaining gaps:

- apps/sim/app/api/tools/sharepoint/upload/route.ts: siteId/driveId were
  not encodeURIComponent-ed in the Graph upload URL, unlike every other
  sharepoint route/tool - same encoding-gap class fixed everywhere else
- read_page.ts: transformResponse recomputed siteId with a raw `||` in
  3 spots instead of the optionalTrim(...) || optionalTrim(...) || 'root'
  precedence used by request.url and every sibling tool
- SharepointBlock (V1, legacy/hidden-from-toolbar but still executable
  for existing saved workflows): listItemFields and listItemId were
  missing `required` for update_list despite the tool requiring them;
  maxPages/groupId/includeColumns/includeItems/nextPageUrl subBlocks
  were entirely absent, making those tool params unreachable from the
  UI; block-level outputs were missing site/pages/content/totalPages/
  nextPageUrl/lists/skippedFiles/skippedCount/errors even though the
  underlying tools return them - V2 already covered all of these
2026-07-02 10:46:35 -07:00
Waleed acece910b4 fix(supabase): remove non-functional SQL introspection, harden storage encoding, add missing endpoints (#5371)
* fix(supabase): remove non-functional SQL introspection path, harden storage URL encoding, add missing storage endpoints

- introspect.ts no longer attempts raw SQL via a nonexistent PostgREST
  RPC endpoint (always failed); now uses the OpenAPI-spec path directly
  with honest heuristic/best-effort documentation for PK/FK/index fields
- storage tools now trim + URL-encode bucket/path segments before
  building request URLs (download, list, get_public_url,
  create_signed_url, delete_bucket, delete, and the upload API route)
- storage_create_signed_url guards against a missing signedURL field
  in the response instead of silently building a broken URL
- add supabase_storage_create_signed_upload_url, supabase_storage_update_bucket,
  and supabase_storage_empty_bucket tools + block wiring
- change insert/upsert `data` param type from 'array' to 'json' to match
  actual accepted shapes (array or single object)
- bump tool versions to semver (1.0 -> 1.0.0)
- rewrite a BlockMeta template that implied unsupported Supabase Auth
  Admin user-provisioning

(cherry picked from commit 52b655acf59bace806c75c06b4b2cfaebe4b6781)

* fix(supabase): address review feedback on update-bucket, signed upload url, and introspect

- storage_update_bucket now fetches the bucket's current config first
  and only overrides isPublic/fileSizeLimit/allowedMimeTypes when the
  caller explicitly provides them, instead of silently forcing
  public: false on every update (the Storage API's PUT is a full
  replace, not a patch)
- storage_create_signed_upload_url and storage_empty_bucket now check
  response.ok before surfacing an error, so a non-2xx response reports
  Supabase's actual error instead of a misleading parse-side message
- introspect.ts drops the spurious ?select=* query param from the
  OpenAPI spec request (meaningless on the root spec endpoint)

(cherry picked from commit 557e4074594464262b2f631ca66272bf60f027f8)

* fix(supabase): tri-state bucket visibility on update, empty-string param coercion, introspect schema header

- introspect.ts now sends Accept-Profile when a schema param is given,
  matching the convention used by the other DB tools, so schema-scoped
  introspection actually reads from that schema's spec
- storage_update_bucket.ts treats an empty-string param the same as
  "not provided" (e.g. an untouched file size limit input no longer
  coerces to 0)
- the shared "Public Bucket" dropdown always sent an explicit true/false
  for storage_update_bucket (its default masked "not touched"), which
  could still flip a public bucket private; added a dedicated
  "Keep Current / True / False" control for the update operation so
  omitting a choice genuinely omits the isPublic override

(cherry picked from commit c80567b980e9a547b892a222cebee2bd03d89420)

* fix(supabase): check response.ok before parsing storage_create_signed_url

Matches the pattern already applied to the new signed-upload-url tool
this session — a non-2xx response now surfaces Supabase's actual error
message instead of the generic "did not return a signed URL path" one.

(cherry picked from commit 9f40427dd80ec21b4af1e85c9c8218fd961d6931)

* fix(supabase): correct download param semantics, echoed path field, and schema hint mismatch

Found by a second, per-tool parallel validation pass against live Supabase/PostgREST docs and source:

- storage_get_public_url.ts and storage_create_signed_url.ts sent
  download=true literally, which Supabase's Storage API treats as a
  filename override (renaming the downloaded file to "true") rather
  than a boolean flag; forcing a download while keeping the original
  filename requires an empty download= value
- storage_create_signed_url.ts also sent download in the POST body,
  which the sign endpoint ignores entirely — forcing download only
  works as a query param on the returned URL
- storage_create_signed_upload_url.ts read a `path` field from the API
  response that doesn't exist there; it now echoes the caller-supplied
  path, matching the official storage-js client
- introspect.ts's nullable heuristic didn't disclose that a NOT NULL
  column with a default is misreported as nullable (PostgREST omits it
  from the OpenAPI required list in that case); documented alongside
  the other already-disclosed heuristics, and removed a tautological
  schema-filter check left over from an earlier revision
- insert.ts/upsert.ts's `data` param reverted from 'json' back to
  'array': the 'json' type mapped to an LLM-facing JSON-schema type of
  'object', which contradicted the param's own description ("array of
  objects or a single object")
2026-07-02 10:45:53 -07:00
943ee27686 feat(langsmith): add run update, get run, and feedback tools (#5363)
* v0.6.29: login improvements, posthog telemetry (#4026)

* feat(posthog): Add tracking on mothership abort (#4023)

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

* fix(login): fix captcha headers for manual login  (#4025)

* fix(signup): fix turnstile key loading

* fix(login): fix captcha header passing

* Catch user already exists, remove login form captcha

* feat(langsmith): add run update, get run, and feedback tools

- add langsmith_update_run (PATCH /runs/{id}) to complete the create-then-patch tracing lifecycle
- add langsmith_get_run (GET /runs/{id}) to read a run back
- add langsmith_create_feedback (POST /feedback) to attach scores/corrections to runs
- wire all three into the LangSmith block, reusing shared subBlocks across operations
- fix feedback-capture template to use real feedback API instead of faking it via tagged runs
- switch manual Object.fromEntries filtering to filterUndefined per repo convention

* fix(langsmith): reject non-numeric feedback score instead of silently sending null

Number() on an invalid score string produces NaN, which serializes to
JSON null and would still be sent to LangSmith. Throw instead, matching
the existing parseJsonValue error pattern.

* fix(langsmith): harden error handling and validation on new run tools

- update_run, get_run, create_feedback now check response.ok before
  parsing/returning, matching the cloudwatch/zoom convention instead of
  silently returning success on a 4xx/404
- block outputs schema now exposes inputs/outputs for Get Run
- update_run requires at least one field to patch, matching the
  batch-ingest guard for post/patch

* fix(langsmith): align get_run/update_run outputs and narrow feedback score type

- get_run now also outputs runId (alias of id) so workflows can read
  the run identifier consistently across all operations on the block
- update_run now parses and surfaces the response message instead of
  discarding the body entirely, matching create_run's pattern
- narrow LangsmithCreateFeedbackParams.score to number — the tool
  param is declared as JSON-schema 'number' and the block's parseScore
  never produces a boolean, so the wider type was dead and misleading

* fix(langsmith): reject empty-string patch fields and empty PATCH bodies

- block mapper now normalizes blank name/end_time/status/error inputs
  to undefined instead of forwarding empty strings, which would clear
  those fields on the LangSmith run
- update_run tool now throws if the filtered PATCH body is empty,
  guarding direct/programmatic tool calls that bypass the block's own
  "at least one field" check

* fix(langsmith): normalize empty-string patch fields at the tool layer too

Direct/agent tool calls bypass the block's own emptyToUndefined guard,
so update_run now normalizes blank name/end_time/status/error itself
before filtering, matching the block-level fix.

---------

Co-authored-by: Theodore Li <theodoreqili@gmail.com>
Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Theodore Li <theo@sim.ai>
2026-07-02 10:45:02 -07:00
Waleed a267a95809 fix(vercel): align integration with live Vercel REST API docs (#5370)
* fix(vercel): align integration with live Vercel REST API docs

- add missing teamId/slug scoping to 29 tools (deployments, checks, projects, env vars)
- add DNS SRV/HTTPS record support (nested srv/https objects) to create/update_dns_record
- add decrypt/gitBranch filters to get_env_vars, autoUpdate to rerequest_check
- add redirect param to create_alias, comment to create_dns_record
- add missing customNameservers/userId/teamId/transferStartedAt fields to list_domains output
- wire pagination filters (limit/since/until/search/app) into block subBlocks for list_deployments, list_teams, list_team_members, list_dns_records, list_project_domains
- wire previously-unexposed tool params into block UI: withGitRepoInfo, gitSource, forceNew, externalId, rerequestable, output, direction, follow
- fix duplicate projectId subBlock id collision between list_deployments and other project-scoped operations
- fix teamId scope field incorrectly hidden for check operations

* fix(vercel): address review findings on DNS/deployment param handling

- fix withGitRepoInfo 'No' dropdown sending withGitRepoInfo=false instead of omitting the param
- remove redundant duplicate 'Forward' option from eventsDirection dropdown
- fix mxPriority being silently dropped in update_dns_record when record type is left unset

* fix(vercel): fix pagination token type mismatch and clarify DNS update UX

- fix list_projects nextFrom being stored as a number despite string typing, which threw TypeError on .trim() when chained into a follow-up list_projects call
- clarify that the DNS update Value field has no effect on existing SRV/HTTPS records unless Record Type is explicitly reselected

* fix(vercel): fix zero-value SRV/HTTPS/MX numeric fields being dropped

- SRV weight/port/priority, HTTPS priority, and MX priority use truthy checks that silently drop legitimate 0 values; switch to explicit empty/null checks
- add missing MX Priority field to create_dns_record block UI (tool already required it for MX records but there was no way to set it)

* fix(vercel): fix stale cross-operation field leaks in block params

- fix list_deployments/create_deployment/update_check/list_team_members/update_dns_record/update_env_var conditionally spreading into keys that collide with unrelated operations' non-destructured literal subBlock fields (projectId, deploymentId, target, name, search)
- a stale value left over from switching operations in the UI would silently leak into the wrong tool call since the conditional spread only overrides when the current operation's own field has a value
- fix now always assigns these keys directly so they deterministically override any stale base value

* feat(vercel): close remaining input/output completeness gaps

- add missing slug (team-slug) param to 13 domain/DNS/alias tools that were skipped in an earlier fix pass, matching the pattern used everywhere else in this integration
- add rootDirectory, nodeVersion, devCommand params to create_project/update_project (verified against Vercel's live OpenAPI spec — high-value fields for monorepo support and runtime pinning)
- surface rootDirectory/nodeVersion in get_project/list_projects outputs, since the API already returns them
- wire all new fields into the block UI as advanced fields

* fix(vercel): fix remaining No->false truthy-string dropdown bugs

- checkRerequestable, checkAutoUpdate, envVarsDecrypt dropdowns used id: 'false' for their No option (a truthy non-empty string), causing the conditional spread to always fire and explicitly send false instead of omitting the param
- swept the whole file for this pattern; checkBlocking correctly keeps 'false' since it's a required field that's always sent directly, not conditionally

* fix(vercel): fix silent project-rename leak in update_project

- update_project had no dedicated rename field and just returned base directly, so a stale value from create_deployment's unrelated 'name' subBlock (Project Name for the deployment) could silently flow through and rename a project on update
- add a dedicated 'New Project Name' field for update_project, wired with a direct override so it always takes precedence over any stale leaked value
2026-07-02 10:39:09 -07:00
Waleed 0507acf2dd fix(amplitude): correct wire formats and add funnels/retention analytics (#5355)
* fix(amplitude): validate integration against API docs, add funnels/retention

- Fix Identify/Group Identify sending JSON instead of the form-urlencoded body Amplitude requires
- Fix Send Event using snake_case product_id/revenue_type instead of Amplitude's camelCase productId/revenueType
- Fix Get Revenue parsing a response shape that never matched the real Revenue LTV API
- Add EU data residency support across all tools
- Add missing filters/formula/segment params to Event Segmentation, groupBy/segment to Active Users and Revenue
- Add first_used/last_used to User Activity output, non_active/flow_hidden to List Events output
- Add real-time/hourly interval options to Event Segmentation
- Add Funnels and Retention tools for conversion and retention analysis
- Harden JSON-shape validation across funnels/segmentation/retention (fail loudly on malformed or partial input instead of silently degrading)
- Expose every tool output field (user_profile, send_event, user_search) on the block so nothing is unreachable downstream
- Update brand colors to current Amplitude guide

* fix(amplitude): validate retention brackets, require formula param, add segmentation 2nd group-by

- Retention now validates retentionBrackets as a JSON array and requires it when retentionMode is "bracket", matching the block UI's requirement
- Event Segmentation now throws if metric is "formula" but no formula is provided, matching the block UI's requirement
- Event Segmentation now supports a documented second group-by property via groupBy2/g2
- Corrected Get Active Users' group-by copy — Amplitude's docs don't document a second-property syntax for /api/2/users the way they do for segmentation's g2, so the field no longer overpromises "max two"
2026-07-02 10:25:59 -07:00
Waleed 59d6b8a62e fix(onepassword): validate integration against API docs, add file downloads (#5365)
* fix(onepassword): validate integration against API docs, add file downloads

- add onepassword_get_item_file tool + route for downloading item file
  attachments (SDK items.files.read / Connect files/{id}/content), backed
  by newly-exposed item.files metadata on get/create/replace/update item
- fix update_item JSON Patch applying array indices instead of 1Password's
  documented field-ID addressing (/fields/{fieldId}/...), which silently
  dropped field edits in Service Account mode
- fix Service Account mode's list-vaults/list-items filter to honor SCIM
  `eq` exact-match semantics instead of always substring-matching
- expand the create-item category dropdown from 9 to 19 real, creatable
  1Password categories (was missing SOFTWARE_LICENSE, EMAIL_ACCOUNT,
  MEMBERSHIP, PASSPORT, REWARD_PROGRAM, DRIVER_LICENSE, BANK_ACCOUNT,
  MEDICAL_RECORD, OUTDOOR_LICENSE, WIRELESS_ROUTER, SOCIAL_SECURITY_NUMBER)
- replace the block's single opaque `response: json` output with typed,
  per-operation output fields matching repo convention
- remove incorrect password-masking on the Vault ID field (not a secret)
- re-export tool types from the onepassword barrel

* fix(onepassword): honor SCIM attribute name in filter matcher

matchesFilter always compared against name/title regardless of the
attribute named in the eq expression, so `id eq "..."` incorrectly
matched against the display name instead of the id.

* fix(onepassword): close output-parity and doc-string gaps from final audit

- restore a deprecated no-op 'response' output so pre-existing saved
  workflows referencing it fail soft (empty) instead of hard-erroring
  now that per-operation outputs replace it
- add missing block outputs (urls, favorite, version, state,
  lastEditedBy) for get/create/replace/update item so all real
  FULL_ITEM fields are discoverable as <Block.field> references
- hide Connect Server credential fields for Resolve Secret (Service
  Account only) instead of leaving them selectable and silently ignored
- correct two doc-string enum lists that advertised values the API
  doesn't return (vault type TRANSFER, item state DELETED)

* fix(onepassword): fix silent data loss in update_item (Service Account mode)

update_item applied user JSON Patch ops (documented/typed against the
Connect-shaped vocabulary get_item returns: label/type/section.id)
directly onto the raw SDK item, whose vocabulary differs (title/
fieldType/sectionId, and SDK category enum strings vs Connect's
SCREAMING_SNAKE_CASE). Most patches beyond /title, /tags/-, and
/fields/{id}/value silently no-opped or could corrupt the item while
still reporting success.

Extracted the Connect->SDK item conversion already used by replace_item
into a shared connectItemToSdkItem helper. update_item now normalizes
the fetched item to Connect shape, applies patches to that, then
converts back before calling items.put() -- matching create/replace's
existing translation pattern.

Found via an adversarial final-verification pass that traced concrete
patch operations by hand against the SDK's actual field vocabulary.

* fix(onepassword): preserve field metadata and empty-title fallback

connectItemToSdkItem rebuilt every field as a bare object, dropping
SDK-only metadata (e.g. password-generation details) that a raw
patch/replace previously left untouched. Now merges onto the existing
SDK field by id before applying the translated properties, and only
starts fields bare when they're genuinely new.

Also restored the || (not ??) fallback on title to match replace_item's
prior behavior of treating an explicitly empty title as "not provided".
2026-07-02 10:23:48 -07:00
Waleed f658e6dc73 fix(tailscale): align tool coverage and outputs with the Tailscale API (#5366)
* fix(tailscale): align tool coverage and outputs with the Tailscale API

- fix list_users profilePicUrl field name (API returns lowercase, was always null)
- add nodeId, keyExpiryDisabled, expires to device outputs
- quote the If-Match header value on ACL updates per API spec
- add set_acl, expire_device_key, suspend_user, delete_user tools
- add wandConfig to dnsServers/searchPaths block fields
- expand BlockMeta skills/templates for ACL and key-expiry workflows

* fix(tailscale): remove phantom magicDNS field from list_dns_nameservers

The GET /tailnet/{tailnet}/dns/nameservers response only returns
{dns: string[]} per the API spec — magicDNS is not part of this
endpoint's response and was always silently false.

* fix(tailscale): extend ToolResponse in expire_device_key response type

Matches the pattern used by the other new tools in this PR
(delete_user, suspend_user).

* fix(tailscale): list_auth_keys now returns all tailnet keys

GET /tailnet/{tailnet}/keys silently scopes to the caller's own
keys unless all=true is passed, contradicting the tool's stated
purpose of listing all auth keys in the tailnet.
2026-07-02 10:15:06 -07:00
Waleed 5966e5cf5a feat(similarweb): add page views tool, fix paid referrals field mismatch (#5354)
* feat(similarweb): add page views tool, fix paid referrals field mismatch

- Add similarweb_page_views tool (Total Page Views, Desktop & Mobile)
- Fix paidReferrals in website overview: API Lite response key is
  literally "paid _referrals" (space before underscore), not caught
  by the existing fallback chain, so it always resolved to null
- Move startDate/endDate/mainDomainOnly to advanced mode

* fix(similarweb): accept both page_views key spellings in page views response

Cursor Bugbot and Greptile both flagged the response field as page_views by
convention, but SimilarWeb's own docs example response uses pages_views for
this specific endpoint. Accept both spellings defensively so the tool works
regardless of which is actually returned.
2026-07-02 10:12:53 -07:00
Waleed e7c9a67194 fix(algolia): tighten tools.config, add geo/facet search + task-status tool; icon/color tweaks (#5356)
* fix(algolia): fix serialization-time param mutation, add geo/facet search, task status tool

- move all tools.config coercion/remapping out of tool() into a proper params() function so dynamic block references aren't destroyed before variable resolution
- wire facets and getRankingInfo into the search tool so those documented outputs are actually reachable
- add geo-search (aroundLatLng/aroundRadius/insideBoundingBox/insidePolygon) to search and browse_records, matching delete_by_filter
- fix aroundRadius param type (string, not number, since it accepts "all")
- sync batch_operations description with the real action set (delete, clear)
- consolidate list_indices pagination into the shared page/hitsPerPage fields instead of duplicate listPage/listHitsPerPage
- add algolia_get_task_status tool so workflows can poll a taskID instead of guessing when a write is applied
- trim indexName/objectID/destination before building request URLs
- add ranking-tuning and index-snapshot skills to AlgoliaBlockMeta

fix(dropcontact): swap icon to the official wordmark's teal swirl mark, bgColor to match

chore(grafana): bgColor to white to match brand tile convention

* fix(algolia): register subblock-id migration for listPage/listHitsPerPage

CI's subblock-id stability check correctly flagged that consolidating
list_indices pagination into the shared page/hitsPerPage fields would
silently drop values from already-deployed workflows. Add the rename
mapping so existing saved state migrates instead of being lost.

* fix(algolia): remove fabricated pendingTask field from get_task_status

Algolia's Get Task Status response (additionalProperties: false) only
returns `status` (published | notPublished) — pendingTask belongs to
the List Indices response, not this endpoint. Drop it from the tool's
output, response type, and block outputs rather than inventing data.

* fix(algolia): coerce getRankingInfo/createIfNotExists/forwardToReplicas from real booleans, not just strings

A wired <Block.output> boolean (e.g. true) failed the `=== 'true'`
string-only checks and silently flipped to the wrong value. Add a
toBool helper that accepts both the dropdown's string values and a
genuine boolean passed in via a dynamic reference.

fix(dropcontact): render icon with currentColor instead of hardcoded fill

The new teal swirl mark's fill (#0ABA9F) matched the block's bgColor
exactly, making the icon invisible on its own tile. Use currentColor
and set iconColor so the shared tile-contrast logic (getTileIconColorClass)
renders it white-on-teal like the rest of the brand icon system.

* fix(algolia): trim indexName in request bodies, not just URL paths

Greptile caught that search.ts's body-level indexName (sent inside the
multi-query POST body, not URL-encoded) wasn't trimmed like every other
tool's URL-path indexName. Fixed there and in get_records.ts's per-request
indexName default/override, which had the same gap.

* fix(algolia): route list_indices and get_task_status GETs to the -dsn read host

Verified against Algolia's official JS client source
(getDefaultHosts + transporter isRead = useReadTransporter || method === 'GET'):
every GET request routes to the read (-dsn) host, matching the other 14
tools in this integration (get_record, get_settings, etc). Both tools
were incorrectly hitting the write host.

* chore(algolia): regenerate docs to drop stale pendingTask entry

The get_task_status pendingTask output was removed from code in
d1021292ec (fabricated field, not in Algolia's real API response) but
docs weren't regenerated at the time, leaving a stale entry. Also
syncs the Dropcontact icon's currentColor fill into the docs mirror.

* fix(algolia): correct batch_operations body requirement wording

Verified against Algolia's actual batchWriteParams schema (specs/common/schemas/Batch.yml):
body is a required property on every batch request item, including
index-level delete/clear actions — it isn't omittable. The tool's
param description previously said to omit it; corrected to say use an
empty object instead.
2026-07-02 10:02:36 -07:00
Waleed 02b1de4faf fix(ahrefs): align tool coverage and outputs with the Ahrefs API v3 (#5367)
* fix(ahrefs): align tool coverage and outputs with the Ahrefs API v3

- 5 of 8 tools were missing the required `select` param (API v3 rejects
  list-endpoint requests without it) and 3 sent a `date` param the
  endpoint doesn't accept
- all list endpoints sent an `offset` param that doesn't exist on any
  Ahrefs v3 site-explorer endpoint (only `limit` is supported)
- domain_rating and backlinks_stats read response fields at the wrong
  nesting level and always returned zeros; several other tools mapped
  output fields to column names the API doesn't return (position, url,
  traffic, backlinks, dofollow_backlinks, http_code) instead of the
  real ones (best_position, best_position_url, sum_traffic,
  links_to_target, dofollow_links, http_code_target)
- keyword_overview used the wrong endpoint param entirely (`keyword`
  instead of `keywords`) so it always returned empty
- added ahrefs_metrics (site-explorer/metrics) and
  ahrefs_organic_competitors (site-explorer/organic-competitors) tools
- fixed docsLink to point at docs.sim.ai instead of ahrefs.com

* fix(ahrefs): default metrics country to us like every other tool

Greptile and Cursor Bugbot both flagged that ahrefs_metrics omitted
the country when unset, unlike every other country-accepting Ahrefs
tool, which silently returns global data instead of US data on
direct tool calls.

* fix(ahrefs): default history to all_time on backlinks and referring domains

Cursor Bugbot flagged that history was only sent when explicitly set,
even though the block UI defaults it to all_time — same class of gap
as the metrics.ts country fix, applied for direct tool-call consistency.

* feat(ahrefs): expose search intent flags on keyword overview

Adds the real intents field (informational, navigational, commercial,
transactional, branded, local) from keywords-explorer/overview, which
was verified valid against the live API docs but not yet surfaced.
2026-07-02 09:49:24 -07:00
Waleed dabb856b9a fix(loops): align integration with live API docs, add suppression + get-template tools (#5358)
* fix(loops): align integration with live API docs, add suppression + get-template tools

- fix list_transactional_emails endpoint URL (was /transactional, now /transactional-emails)
- fix response fields to match actual API schema (createdAt/updatedAt, not the never-existent lastUpdated)
- add loops_check_contact_suppression, loops_remove_contact_suppression, loops_get_transactional_email tools
- wire new tools into block operations, outputs, and registries
- alphabetize tools/registry.ts loops entries

* fix(loops): expose contactId output, fix stale tool description

- add missing contactId block output for check_contact_suppression (Greptile P1)
- fix list_transactional_emails description to mention createdAt (Greptile P2)

* fix(loops): restore lastUpdated as backwards-compat alias on list_transactional_emails

Final validation pass found that /api/v1/transactional (the endpoint this tool
used before this PR) is a real, functional, deprecated Loops endpoint whose
schema genuinely returns lastUpdated - it was not a broken/invented field.
Migrating to /api/v1/transactional-emails is still correct (current endpoint,
better error semantics), but dropping lastUpdated would break any existing
workflow reading it from this block's output. Keep it as a deprecated alias
of updatedAt alongside the new createdAt/updatedAt fields.

* fix(loops): update id output description to cover get_transactional_email
2026-07-02 09:48:20 -07:00
Waleed 7fd89bca35 fix(brex): surface isPproEnabled in transfer block outputs (#5373)
Tool-level get_transfer/list_transfers already returned is_ppro_enabled
(confirmed present on Brex's live Transfer schema), but the block's
outputs map didn't declare it, so it was unreachable from the workflow
UI. Adds it alongside the other transfer output fields.
2026-07-02 09:06:12 -07:00
Waleed 5553c449af improvement(brex): fill validate-integration gaps against live API docs (#5362)
- add wandConfig AI-autofill to expense/spend-limit filter subBlocks
- surface is_ppro_enabled on get/list transfer tools
- surface start_date/end_date/authorization_settings on list_spend_limits to match get_spend_limit
2026-07-02 08:29:29 -07:00
Waleed 220da449c9 fix(mothership): stop inlining full execution traces for the logs context (#5353)
* fix(mothership): stop inlining full execution traces for the logs context

Tagging a run via "Troubleshoot in Chat" (or any @-mention of a logs
context) resolved through processExecutionLogFromDb, which materialized
the ENTIRE execution trace (every block's input/output, nested tool-call
spans) and inlined it directly into the prompt. For any non-trivial run
this repeatedly blew the context window, forcing multiple compactions and
eventually auto-stopping the agent before it could investigate anything.

Every other context resolver in this file already avoids this by sending
a lightweight pointer instead of a full inline dump (workflow/blocks/
workflow_block contexts point into the VFS). Logs contexts have no VFS
materialization to point at, but the equivalent lightweight mechanism
already exists as a tool: query_logs supports incremental disclosure
(overview for timing/cost, full for a scoped block's input/output, or
pattern to grep the trace) and is already registered for the mothership
agent.

Now processExecutionLogFromDb sends a compact summary (id, workflow,
level, trigger, timing, cost) plus a note pointing the model at
query_logs with the executionId, instead of materializing and embedding
the trace. Also drops the now-unused executionData column from the
select projection, so resolving a logs context no longer fetches a
potentially large JSONB blob it never reads.

* improvement(mothership): send a bounded block overview instead of a bare tool pointer

Follow-up to the previous commit's fix (stop inlining full execution
traces). A pure text pointer telling the model to call query_logs made
the agent's very first useful action against a tagged run contingent on
it noticing and correctly acting on prose in a JSON blob it may only
skim — every sibling resolver in this file instead returns a
deterministic mechanism (a VFS path) the model reads on demand.

There's no VFS materialization for individual execution logs, but the
same deterministic signal is available cheaply: toOverview() (the exact
projection query_logs's own "overview" view already returns) walks the
raw trace spans and produces a compact tree — block name/type/status/
timing/cost, no input or output — without touching large-value refs at
all. The summary now includes that tree, so the model can see which
block failed on the first turn, and the note narrows to what still
requires a tool call: a block's actual input/output/error, or a grep.

materializeExecutionData is still called, but it's a no-op for the
common inline case (it only unwraps a top-level object-storage pointer
for runs whose whole trace was offloaded as one blob) and was needed to
reach traceSpans at all for those heavier runs — exactly the runs most
worth an overview.

A serialized-size cap (mirroring query-logs.ts's own truncation
fallback, scaled down since this lands in the prompt unconditionally)
drops the overview if a pathological span count pushes it over budget,
falling back to the note alone.

Extends the tests: the happy path now asserts the overview tree is
present and that no raw input/output payload leaks into the serialized
summary, plus a new test for the size-cap fallback.
2026-07-01 22:50:26 -07:00
Waleed b34608856d fix(logs): right-size and reorganize log-detail action chips (#5352)
Two concrete regressions from the earlier Chip conversion (#5341):
- Both chips used variant='primary' (the solid inverse-fill treatment),
  the heaviest chrome in the design system, reserved for a single standout
  action per context (Save, Upgrade, Add key) — never a peer among several
  row actions. Two solid pills stacked in a narrow side panel read as
  oversized. Switched both to the bare (default) chip, matching every
  analogous row action in Settings.
- The Version badge was size='md' while its siblings (Level, Trigger) are
  size='sm' — an inconsistency. Aligned to 'sm'.

Also folded the floating 'Troubleshoot in Chat' chip into the Details card
as its own row (matching 'Snapshot' exactly) instead of leaving it orphaned
below Workflow Output — every row in the card now shares one shape (label
left, trailing content right), consistent top to bottom.
2026-07-01 19:46:22 -07:00
Waleed e01123f715 feat(blog): add CTA band above footer (#5350)
- reuse the shared Cta component (same "Build your first agent
  today" band used on the homepage) at the end of the blog listing
2026-07-01 19:14:55 -07:00
Theodore LiandClaude Opus 4.8 dcc7c0c7c4 ci(dev): auto-deploy Trigger.dev tasks + fail dev db:push on TTY prompt (#5343)
* ci(trigger): auto-deploy Trigger.dev tasks to dev-sim on dev pushes

Add a deploy-trigger-dev job that runs `trigger.dev deploy --env preview
--branch dev-sim` on pushes to the dev branch, replacing the manual step.
Gated after migrate-dev for the same reason as build-dev: the new task
code runs against the dev DB, so the schema must be pushed first.

Uses Trigger.dev's remote build (no --local-build), so the runner needs
no Docker/buildx. Requires a TRIGGER_ACCESS_TOKEN repo secret.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015orsjbLX34FPFGujSK3AQK

* ci(trigger): source TRIGGER_PROJECT_ID from repo secret

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015orsjbLX34FPFGujSK3AQK

* ci(trigger): fail fast when Trigger.dev secrets are unset

Guard the deploy step so a missing TRIGGER_ACCESS_TOKEN or
TRIGGER_PROJECT_ID exits with a clear message instead of a cryptic
trigger.dev CLI error, matching the DATABASE_URL guard in migrations.yml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015orsjbLX34FPFGujSK3AQK

* ci(migrations): fail dev db:push on interactive prompt or error

drizzle-kit push prompts interactively for ambiguous renames (--force only
covers data-loss). In CI there's no TTY, so the prompt reads EOF and drizzle
can exit 0 without applying — the job goes green while the schema change was
silently skipped. Close stdin, reject prompt markers, and require a success
marker so an unresolved rename or failed statement fails the job.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015orsjbLX34FPFGujSK3AQK

* ci(migrations): fail dev db:push when drizzle-kit hits a TTY prompt

drizzle-kit push needs a TTY to resolve ambiguous renames; in CI it throws
"Interactive prompts require a TTY terminal" but still exits 0, so the job
went green without applying the schema (e.g. run 28415609570). Fail on that
explicit error. Keys on drizzle's own stable message rather than fuzzy prompt
text, and a real non-zero exit still fails via set -e.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015orsjbLX34FPFGujSK3AQK

* ci(trigger): scope the access token secret as DEV_TRIGGER_ACCESS_TOKEN

The PAT is only used by the dev deploy job, so prefix it DEV_ to match the
repo's dev-scoped secret convention. TRIGGER_PROJECT_ID stays unprefixed —
it's the shared project (same one prod uses); dev-sim is a preview branch
within it, not a separate project.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015orsjbLX34FPFGujSK3AQK

* Adjust warning error

* ci: align build-dev checkout to v6 to match the other jobs

build-dev was the only job still pinning actions/checkout to the v4 hash;
every other job uses v6. Non-functional consistency fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015orsjbLX34FPFGujSK3AQK

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 22:02:59 -04:00
Theodore Li 69b81a679b feat(data-retention): granular PII redaction stages (input + block outputs) (#5272)
* feat(data-retention): granular PII redaction stages (input + block outputs)

* fix(data-retention): propagate block-output redaction into child workflows

* fix(data-retention): close block-output redaction gaps on streaming + resume

* fix(data-retention): drain+mask streamed output, resolve PII policy unconditionally (no fail-open)

* test(testing): support leftJoin().where().limit() in shared db mock

* fix(data-retention): mask agent/Pi memory writes under block-output redaction

* fix(data-retention): guard partial PII stages in GET normalize

* fix(data-retention): mask seeded memory messages under block-output redaction

* fix(guardrails): fail closed on misaligned Presidio batch responses

* fix(data-retention): enabled stage with no entity types redacts all (no fail-open)

* fix(data-retention): reject enabled stage with no entity types; empty = off everywhere

* docs(data-retention): note resume remask covers inline values only

* fix(data-retention): scrub offloaded large-value refs from logs when block-output redaction is off

* fix(data-retention): hydrate, mask, and re-store large-value refs in logs (preserve redacted content)

* fix(data-retention): always apply logs policy to large-value refs when logs stage is on

* perf(data-retention): drop redaction byte ceiling, parallelize chunks (env-tunable), remove request timeouts, sync large-value walk

* feat(data-retention): gate granular PII stages behind pii-granular-redaction flag

- New pii-granular-redaction feature flag (fallback PII_GRANULAR_REDACTION),
  layered on pii-redaction, gating the execution-altering input + block-output stages
- Route returns piiGranularRedactionEnabled and rejects enabling granular stages when off
- UI shows only the Logs stage tab unless the flag is on; clamps active stage
- Drop the per-search Select all toggle; add a Deselect all action to the PII section header

* docs(pii): describe Presidio as a standalone service, not a sidecar

Presidio now runs as its own ECS service (and, in Helm, its own Deployment +
Service) reached over the network via PII_URL — not a sidecar in the app task.
Update README, code comments, env docs, Dockerfiles, and the Helm chart docs to
match, and note the deploy requirement that PII_URL must be reachable.

* fix(data-retention): re-mask offloaded large-value refs on resume + don't lock out granular saves

- Resume/run-from-block restore now hydrates → masks → re-stores large-value refs
  in restored blockStates (not just inline strings), so a value offloaded before the
  block-output stage was enabled can't warm raw PII into downstream blocks. Fails fast.
- pii-large-values: add onFailure mode (throw on the execution path, scrub for logs)
  and redactLargeValueRefsInValue for arbitrary (non-RedactablePayload) values
- Granular flag gate now rejects only NEW off→on granular enablement, so orgs that
  already configured granular stages can still save retention settings when the flag is off
2026-07-01 21:47:02 -04:00
Theodore Li 74571840cd fix(knowledge): surface KB description validation errors and raise limit to 10k (#5347) 2026-07-01 18:29:45 -07:00
Waleed 8d6cec114a feat(blog): add five blogs (#5268)
* feat(blog): add five AI agent articles

Add how-to-create-an-ai-agent, ai-agent-vs-chatbot, ai-agents-vs-rpa, ai-agent-ideas, and best-zapier-alternatives posts.

* fix(blog): correct ai-agent-ideas pronoun and complete zapier TL;DR

Fix 'everyone' -> 'every idea' in ai-agent-ideas intro; add the four missing tools (Power Automate, Pipedream, Relay.app, Integrately) to the best-zapier-alternatives TL;DR so it lists all 10.

* fix(blog): remove HIPAA compliance claims

We are not HIPAA compliant; drop the claim from ai-agents-vs-rpa and best-zapier-alternatives, keeping the SOC2 claim.

* feat(blog): add cover images for new posts, refresh older post covers

- Add cover.jpg for the 5 new AI agent blog posts
- Replace existing cover art for enterprise, mothership, v0-5, multiplayer,
  executor, series-a, and openai-vs-n8n-vs-sim
- Standardize openai-vs-n8n-vs-sim's og image to cover.jpg (was workflow.png)
- Update ogImage frontmatter paths to match

* fix(blog): move new post dates closer to today

Shift the 5 new AI agent posts from June 23-27 to June 27 - July 1,
keeping the same order and 1-day spacing, ending on today's date.

* fix(blog): use current Sim terminology instead of legacy Mothership/Copilot names

Per the constitution language rules, Sim's own natural-language agent
surface is "Chat" and the agent you talk to is "Sim" — not "Mothership"
or "Copilot" (those are prior internal names, still used correctly in
the historical announcement posts but not for new content). Leaves
references to Zapier's and Microsoft's own "Copilot" products as-is
since those name real third-party features.
2026-07-01 18:28:41 -07:00
Waleed 26fb6875e0 fix(chat): fix secret-input chat widget reshaping after submit (#5346)
SecretReveal (the post-submit redacted state) used bespoke h-9/rounded-md/px-2.5
chrome with an absolutely-positioned copy button, instead of the canonical
chip-field chrome (h-[30px]/rounded-lg/px-2) that SecretInput (the pre-submit
state) uses. That mismatch made the credential-paste widget visibly resize
right after saving. Rebuilt SecretReveal on the same chipFieldSurfaceClass
tokens as ChipInput, with the copy button as an inline trailing adornment.
2026-07-01 18:16:37 -07:00
Waleed 577b402636 fix(chat): scope troubleshoot handoff to its workspace (#5344)
* fix(chat): scope troubleshoot handoff to its workspace

MothershipHandoffStorage now records the target workspaceId and only the
matching workspace consumes (and clears) it; a different workspace leaves it
untouched for its owner. Prevents a workspace-A handoff from firing in
workspace B, where A's executionId can't resolve and the run context is
dropped.

* test(chat): cover legacy no-workspaceId handoff tombstoning
2026-07-01 17:57:42 -07:00
Waleed 4df352fb72 fix(integrations): resolve Server Components crash on the integration detail page (#5345)
Root cause (confirmed via staging server logs, digest 783665031): the [block] page.tsx is a server component whose Suspense fallback rendered <ChipLink leftIcon={ArrowLeft}> — passing the lucide icon (a function) across the server->client boundary, which React rejects ("Functions cannot be passed directly to Client Components"). This surfaced as the integrations error boundary / "Failed to load integrations" on soft navigation to a detail page. Move the fallback into a client component so the icon never crosses the boundary.
2026-07-01 17:57:17 -07:00
Waleed bca7f2c9b3 feat(logs): add Troubleshoot in Chat button for errored runs (#5341)
* feat(logs): add Troubleshoot in Chat button for errored runs

Errored log runs now surface a "Troubleshoot in Chat" action in the log
details panel. It tags the failed run as a logs context (executionId) and
auto-sends a message to Chat asking Sim to investigate and fix the error,
porting the old copilot "Fix in Chat" behavior to mothership and adding
run-ID tagging.

Cross-route handoff rides a one-shot MothershipHandoffStorage consumed once
on the home surface mount, so the tagged run + prompt survive the navigation
from Logs to Chat and the agent receives the full run error via the resolved
logs context.

* fix(logs): deliver troubleshoot to a mounted chat + harden handoff

Review follow-ups:
- Same-route case (Cursor): LogDetailsContent is also embedded in the Chat
  resource panel, where router.push('/home') doesn't remount Home, so the
  mount-only handoff consume never fired. Generalize the existing
  sendMothershipMessage event to carry contexts and be cancelable: deliver
  straight to a mounted chat when one claims it, and only persist + navigate
  when none is listening.
- Corrupted-entry tombstone (Greptile): consume now clears whenever any entry
  exists, so a malformed/expired handoff can't linger across future mounts.
- Silent store failure (Greptile): only navigate when the handoff actually
  stored, so a failed write never strands the user on an empty chat.

* docs(logs): convert inline comments to TSDoc on declarations

* style(logs): match Troubleshoot button icon gap to View Snapshot sibling

* improvement(logs): use Chip for log-detail action buttons

Aligns the log-details panel's labeled action buttons (View Snapshot,
Troubleshoot in Chat) with the settings design language by swapping the
emcn Button for the canonical Chip pill. variant='primary' preserves the
prior filled emphasis; leftIcon keeps the icons canonical.

* fix(chat): only consume troubleshoot handoff on the new-chat surface

Gate the mount-time handoff consume on `!chatId` so an existing
`/chat/[chatId]` mount can't claim a pending handoff if navigation races —
a handoff always targets a fresh chat.

* fix(logs): hover only the interactive Run ID row in log details

The detail-card rows all hovered to --surface-2, but the card itself is
--surface-2, so the hover was a no-op in light mode and only showed in dark.
It also implied clickability on static readout rows. Now only the clickable
Run ID row hovers, using the canonical --surface-active token; static rows
carry no hover.

* improvement(logs): use emcn Badge for the version pill

Replaces the hand-rolled version span with the canonical Badge
(variant='green' size='md', pixel-identical tokens), so all three detail
badges (Level, Trigger, Version) render through the same component.
2026-07-01 17:19:45 -07:00
Waleed 507cee1187 fix(integrations): repair corrupt icons, backfill missing block metas, restore scroll on back-nav (#5342)
* fix(integrations): repair corrupt icons, backfill missing block metas, restore scroll on back-nav

- Restore 7 brand icons (Google, Outlook, MongoDB, Postgres, OpenRouter, Groq, Cerebras) whose SVG path data was corrupted by a past bulk reformat, flooding the integrations page console with <path> parse errors; add a check:icon-paths CI gate that validates every icon d attribute (operand counts + arc flags).
- Backfill BlockMeta (tags/url/templates/skills) for postgresql, mysql, ssh, sftp, smtp — previously catalog integrations with empty detail pages; add an integration meta-coverage CI check so every catalog block must have a meta.
- Add scroll-position restoration for the integrations index/detail inner scroll containers so browser Back returns to where you were.
- Remove the error digest pill from the shared workspace ErrorShell (kept in logs, dropped from UI).

* fix(integrations): make scroll restoration robust — value-based echo detection + Back/Forward-only gate

Addresses review: replace the racy programmatic-scroll flag with value comparison (a restore's echo equals lastApplied and is ignored, so a stuck flag can never drop the first user scroll or overwrite the saved target), and gate restoration on popstate history traversals so fresh push navigations open at the top instead of jumping mid-list. TSDoc-only comments.

* fix(ci): attribute icon-path errors for export-const icons too

Greptile review: iconNameAt only matched 'export function', so a malformed path inside an 'export const XxxIcon = (...)' arrow-function icon would be misattributed to the preceding function-declared icon. Match both forms (mirrors check-bare-icons indexIconBodies).
2026-07-01 16:58:45 -07:00
Waleed 4e8b88f0af fix(landing): avoid unpolyfilled ES2023 array methods in client code (#5340)
toSorted/toReversed require Safari 16+/iOS 16+ and Next.js/SWC does not polyfill prototype methods (vercel/next.js#58421 closed unmerged), so #5326 broke sorting on the models page and landing preview for Safari 15/iOS 15 with a runtime TypeError. Revert the 6 call sites to [...arr].sort()/[...arr].reverse() (immutable, universally supported, matches the existing codebase idiom) and drop the ES2023 tsconfig lib override that only existed to type-check them.
2026-07-01 16:27:02 -07:00
Theodore Li 62acc9c53f fix(interfaces): always-light public surfaces (chat, resume, file shares, unsubscribe, invite) with emcn components (#5336)
* fix(interfaces): pin deployed chat + resume to light mode, emcn-only components

* fix(interfaces): review feedback — formErrors dep in field renderer, keep overlay tint on attachment remove

* improvement(interfaces): chat header uses the platform SimWordmark instead of the svg asset

* fix(interfaces): light mode for file-share gates, unsubscribe, and invite surfaces
2026-07-01 19:20:40 -04:00
Theodore Li 44215126f4 improvement(tables): make max row size env-configurable and raise default to 400KB (#5338) 2026-07-01 19:10:25 -04:00
Waleed eb367def31 fix(demo): unblock Cal.com booking embed and align work-email validation (#5335)
* fix(demo): unblock Cal.com booking embed and align work-email validation

- Exclude /demo from cross-origin isolation headers (COEP credentialless /
  COOP same-origin) that degraded the third-party Cal.com booking iframe,
  mirroring the existing Google Drive Picker exclusion; the booker no longer
  loads under a Storage-Access handshake that often never finished
- Pin the Cal embed theme/layout in the inline config to fix a dark-on-light
  theme race; keep cal('ui') to UI-only settings
- Gate the demo form's Continue on the same work-email rule the server
  enforces via a shared isFreeEmailDomain helper, bundle-isolated in
  lib/messaging/email/free-email.ts so it doesn't bloat other email bundles
- Trim redundant comments in the demo components

* fix(demo): match /demo subroutes in permissive COEP rule

Align the permissive-headers positive match with the strict-COEP negative
lookahead so any future /demo subroute still receives the permissive
COEP/COOP policy instead of falling through to no headers.

* fix(demo): use demo.* in permissive COEP rule (valid route source)

The demo(/.*)? form introduced a nested capturing group, which Next's
path-to-regexp route-source parser rejects ('Capturing groups are not
allowed'), failing the build. demo.* mirrors the strict-rule lookahead's
demo prefix without a nested group, matching the existing w/.* style.
2026-07-01 14:41:48 -07:00
Waleed 2c5ee4a8db chore(rules): canonical useState prev-tracker for render-phase adjust + no-render-in-render caveat (#5333)
Two recurring pitfalls surfaced by the /w react-doctor pass, verified against
react.dev:
- sim-hooks.md: adjusting state on a prop transition uses the React-canonical
  useState prev-value tracker (NOT a useRef — React forbids reading/writing
  ref.current during render; the react-hooks 'refs' lint flags it, and useState
  is concurrent-safe). The tracker's initial value decides mount behavior
  (sentinel vs current value) — a mis-seed silently drops the mount action. The
  existing useRef variant is noted as discouraged for new code.
- sim-components.md: no-render-in-render is a false positive for a helper called
  inline (reconciled by position, no remount); extract only when mechanical.

Refs: react.dev useState 'Storing information from previous renders'; useRef
'Do not write or read ref.current during rendering'; react-hooks 'refs' lint.
2026-07-01 13:50:32 -07:00
Waleed c923de6a19 feat(providers): add Claude Fable 5 model (#5334)
Re-add claude-fable-5 (Anthropic's most capable widely released model), not marked recommended. Verified against live Anthropic API + docs:
- Pricing $10/$50 per MTok, $1 cached input (cache-read); 1M context; 128k max output; GA 2026-06-09
- Adaptive thinking only (always on, cannot be disabled) — routed via supportsAdaptiveThinking; manual budget_tokens/temperature are rejected with 400
- effort levels low/medium/high/xhigh/max (default high)
- nativeStructuredOutputs confirmed supported (live output_format json_schema call returned 200), matching same-generation siblings (Sonnet 5, Opus 4.8)
2026-07-01 13:50:15 -07:00
Waleed 9204b4a248 improvement(knowledge): react-doctor perf pass across the knowledge base module (#5332)
* improvement(knowledge): react-doctor perf pass across the knowledge base module

* improvement(knowledge): drop stable mutate fn from useCallback deps

* docs(rules): tighten sim-react-performance mutation + toSorted wording
2026-07-01 13:23:24 -07:00
Waleed 76537c890c improvement(files): react-doctor performance pass on the files module (#5330)
* improvement(files): react-doctor performance pass on the files module

* fix(files): use spread-sort not toSorted in client (Safari<16/iOS15 crash)

SWC does not polyfill Array.prototype.toSorted and the repo sets no
core-js/browserslist target, so it throws on Safari <16 / iOS 15. Revert
the two client-side toSorted calls to [...arr].sort() and correct the
harness guidance in sim-components.md accordingly.
2026-07-01 13:15:17 -07:00
Waleed 671535469d improvement(tables): react-doctor safe performance pass (#5329)
* improvement(tables): react-doctor safe performance pass

Apply high-confidence, behavior-preserving perf fixes surfaced by
react-doctor on the tables module:

- Lazy-init the import-status ref so it stops allocating and discarding
  a fresh Map on every render (rerender-lazy-ref-init).
- expandToDisplayColumns: build an outputs-by-column-name Map once per
  workflow group instead of Array.find() per column (js-index-maps).
- Sort the tables list with toSorted() instead of [...list].sort()
  (js-tosorted-immutable; ES2023 lib is enabled for apps/sim).
- Hoist the static TITLE_BY_MODE map and the pure editor wheel handler
  to module scope so they are not rebuilt each render.

Left the effect/selection refactors (state-synced-to-prop, effect
chains) untouched: those effects react to async-loaded rows and are not
the copy-a-prop-into-state anti-pattern; refactoring them needs live
grid verification. Barrel-import findings are false positives against
the repo's mandated barrel-import convention.

* improvement(tables): preserve find() first-match semantics + document perf idioms

- expandToDisplayColumns: build the outputs-by-column-name index with an
  explicit first-write-wins loop so it is provably identical to the prior
  Array.find() on the (schema-precluded) duplicate-columnName case, not
  last-wins as new Map(entries) would be.
- Add .claude/rules/sim-react-performance.md capturing the behavior-
  preserving render-perf idioms applied here (lazy object refs, module-
  scope hoisting, Map/Set pre-indexing, immutable array methods with the
  ES2022/ES2023 lib caveat, local-barrel false-positive note), and point
  to it from CLAUDE.md.

* fix(tables): revert toSorted to spread-sort for universal browser support

Array.prototype.toSorted is ES2023 runtime; SWC does not polyfill it and
the default browserslist still includes browsers without it (iOS 15
Safari), so the tables page would crash there. [...result].sort() is
non-mutating (sorts a copy, never touches the React Query cache array)
and works everywhere; the perf delta is negligible. Correct the
sim-react-performance rule doc to reflect that tsconfig lib only affects
type-checking, not runtime availability.

* refactor(tables): tidy lazy ref init and align perf-rule cross-reference

- use-workspace-imports: lazy-init the status Map at its point of use in
  the effect ((ref.current ??= new Map())) instead of mutating the ref
  during render with a separate in-effect fallback. The ref is only read
  in the effect, so this removes the render-phase write and the dead
  '?? new Map()' branch while keeping the identical persistent Map.
- CLAUDE.md: fix the render-performance pointer so it matches the rule
  doc — toSorted/toReversed are unsafe on client render paths (SWC does
  not polyfill prototype methods), not merely an ES2022/ES2023 lib note.
2026-07-01 13:11:07 -07:00
Waleed 8cebeb6774 improvement(chat): code-split resource preview panel out of initial /chat bundle (#5331)
* improvement(chat): code-split resource preview panel out of initial /chat bundle

Lazy-load the MothershipView resource-preview panel (file-viewer, rich-markdown,
CSV/PDF stack) via React.lazy + local Suspense so it is not in the initial /chat
bundle; it only renders once a chat has messages. Remove its now-dead barrel
re-export so the split takes effect (this app has no sideEffects:false, so a
leftover barrel edge drags the heavy module back into the initial chunk).

Also a small behavior-preserving perf pass on the input surface:
- toSorted (non-mutating) instead of spread+sort in use-skill-auto-mention
- Map first-match lookup instead of .find()-in-loop in prompt-editor

Document the code-split/barrel gotcha in .claude/rules/sim-imports.md.

* fix(chat): drop unsafe toSorted, keep code-split + Map lookup

toSorted (ES2023) is not polyfilled by SWC and crashes iOS15/Safari<16 in
client bundles, so revert use-skill-auto-mention to the non-mutating
[...arr].sort() it had. The code-split of MothershipView and the first-match
Map lookup in prompt-editor are unaffected. Tighten the sim-imports code-split
note to state webpack *can* retain the barrel edge (removing the dead re-export
is the guaranteed fix).
2026-07-01 13:07:58 -07:00
Waleed 42e3a038a0 improvement(landing): react-doctor health pass across the landing surface (#5326)
* improvement(landing): react-doctor health pass across the landing surface

- shared JsonLd server component with HTML-safe serialization (replaces 32 inline dangerouslySetInnerHTML JSON-LD sites; structured-data output semantically identical, XSS hardened)
- state/effect fixes: derive-instead-of-sync, phase state machines, useSyncExternalStore for media query, handler-only state to refs
- structural splits for one-component-per-file and non-component export isolation
- mechanical: combined iterations, hoisted Intl formatters/static values, immutable sorts, stable keys, a11y labels, focus rings, SVG precision
- ES2023 lib for apps/sim to allow toSorted/toReversed

* fix(landing): harden auth-modal async open (dismiss race + disabled-signup redirect)

- guard the pending provider-status fetch with openRequestedRef so a late resolve can't reopen the modal or re-fire the opened event after dismiss
- open synchronously (loader) when status is still loading so the click stays responsive
- route to /login instead of /signup when registration is disabled and no modal providers are available

* fix(landing): reset demo step index when the preview auto-cycle restarts

- reactive isDesktop can re-run the demo effect on a 1024px resize; reset demoIndexRef to 0 so applyDemoStep(step 0) and scheduleNextStep read the same step, instead of replaying from a stale index and skipping beats
- clarify why the chat reveal's previous-value ref is safe here (timer-driven, no concurrent-interruption boundary)

* fix(landing): single-shot auth open, stable connector keys, exact prompt-row lookup

- auth-modal: consume openRequestedRef on resolve so a queued double-click (or the shared mount prefetch) can't run openWithStatus twice (no duplicate auth_modal_opened / redundant setView)
- landing-preview-knowledge: key connector icons by a stable slug instead of a component name that can be mangled/emptied under minification
- workflow-data getEditorPrompt: single ordered pass preserves the original find() first-match-in-order semantics (Map lookup had preferred 'Prompt' over an earlier 'System Prompt')

* style(landing): convert added inline comments to TSDoc-style
2026-07-01 12:12:49 -07:00
Waleed c957aa2b94 improvement(settings): react-doctor perf & correctness pass (#5327)
* improvement(settings): react-doctor perf & correctness pass

- combine multi-pass array iterations into single passes (api-keys, credential-sets, secrets-manager, team-management)
- cache/hoist Intl formatters to module scope (billing); hoist pure functions and inline-default constants
- stabilize react-query array fallbacks so memos stop recomputing while loading (api-keys, byok, workflow-mcp)
- fix create-workflow-mcp modal reset via render-phase prevOpen compare instead of a state-adjusting effect
- accessibility: aria-labels on real inputs, aria-hidden on autofill decoys, native <button> for clickable rows
- immutable in-place sort where the array is already a fresh copy

* fix(settings): render non-clickable inbox rows as div, not disabled button

Addresses review: a native disabled <button> can inherit browser disabled
styling (dimmed text / lower contrast) on non-navigable task rows. Render an
interactive <button> only when the row is clickable; otherwise a plain <div>
with identical layout — preserving the semantic-button a11y win without the
disabled-state visual regression.
2026-07-01 12:06:54 -07:00
Waleed 78661d25ff fix(providers): drop deprecated temperature capability from claude-sonnet-5 (#5328)
Claude Sonnet 5 rejects the temperature parameter with a 400 ("`temperature` is deprecated for this model"), verified against the live Anthropic API. The model entry exposed temperature: { min: 0, max: 1 }, so supportsTemperature() returned true and the Anthropic request builder sent temperature whenever thinking was disabled, breaking those runs. Remove the capability (matching Opus 4.7/4.8, which omit it for the same reason) so temperature is never sent for Sonnet 5. Add a supportsTemperature regression assertion.
2026-07-01 11:34:01 -07:00
Waleed af87de09a7 fix(connectors): allow self-hosted private DB hosts via opt-in flag (#5322)
* fix(connectors): allow self-hosted private DB hosts via opt-in flag

Database/connector tools rejected any host resolving to a private/reserved/
loopback IP, blocking the common self-hosted topology where the DB is reached
by a Docker/K8s/Swarm service name. Add an opt-in ALLOW_PRIVATE_DATABASE_HOSTS
flag that bypasses the private-host block in validateDatabaseHost while still
resolving and pinning DNS. Blocked on the hosted platform regardless of the env
var, mirroring DISABLE_AUTH.

Fixes #4319

* fix(connectors): pin postgres IP in all ssl modes; strip IPv6 brackets

Address review on #5322:
- validateDatabaseHost now strips surrounding IPv6 brackets before the
  localhost/private-IP checks and DNS lookup, so a bracketed loopback like
  [::1] is classified correctly instead of failing as unresolvable.
- PostgreSQL connector always connects to the validated, pinned IP (removed
  the ssl='preferred' carve-out that passed the original hostname and let the
  driver re-resolve during connection). Matches the MySQL/MongoDB pin pattern.
- Add postgres connector pinning tests and bracketed-IPv6 host tests.

* fix(connectors): rename flag to isPrivateDatabaseHostsAllowed; trim comment

- Rename env-flag const to satisfy the env-flags 'is' prefix CI check
  (env var ALLOW_PRIVATE_DATABASE_HOSTS is unchanged).
- Tighten the postgres pinning comment to a single line.
2026-07-01 11:19:08 -07:00
Waleed b8e88b14b7 improvement(settings): react health pass across settings surface (#5324)
- workflow-mcp: move tool-edit state seeding from a derive effect into the Edit event handler
- admin: drop 2 inert useMemo (page math) and 1 unused useCallback
- inbox + teammates: migrate filter/search view-state from useState to nuqs URL params (shareable, debounced writes)
- team roster: memoize O(workspaces x members) grouping so keystroke search stays cheap at scale
- remove dead code (unused props/locals: currentUserEmail, setOrgName, isLoadingWorkflows, isTeam)
2026-07-01 11:02:08 -07:00
Waleed 0575875be1 fix(custom-tool): restore modal body scroll so Save stays reachable (#5321)
* fix(custom-tool): restore modal body scroll so Save stays reachable

The Edit/Create Agent Tool modal clipped its footer (Save/Update) and could not scroll with long code or schema content.

Root cause: #4354 migrated the modal to ChipModal and changed the body from a scroll region (flex-1 overflow-y-auto) to flex-none overflow-visible so the hand-positioned EnvVar/Tag autocompletes could spill past it. That removed the scroll region, so tall content grew the body past the modal's max-h-[84vh] cap and the overflow-hidden surface clipped the footer.

Restore ChipModalBody as the scroll region (its documented behavior) and switch the EnvVar/Tag dropdowns to portaled inputRef caret-anchoring, matching the canonical Function-block editor, so they anchor to the caret in a portal and are never clipped by the scroll boundary.

* fix(custom-tool): keep dropdown anchors content-relative on body scroll

Review follow-up. The autocomplete popovers already portal their menus (never clipped by the body's scroll boundary), so the fix is only to restore the body as the scroll region. Reverting the dropdowns to their content-relative absolute anchors keeps them glued to the caret while the body scrolls; the caret-viewport inputRef anchoring used fixed viewport coordinates that only refreshed on edits, detaching the menu on scroll.
2026-07-01 10:59:22 -07:00
Waleed 33f9d645ba fix(careers): remove /careers redirect so the in-app page is reachable (#5320)
A pre-existing permanent redirect /careers -> jobs.ashbyhq.com/sim shadowed the
new first-party careers page (#5316), sending visitors straight to the Ashby
board. Drop it so /careers serves the in-app page (which itself pulls the Ashby
roles).
2026-07-01 09:54:31 -07:00
Waleed 2393b72ee6 feat(careers): careers page backed by the Ashby job board (#5316)
* feat(careers): careers page backed by the Ashby job board

* fix(careers): harden Ashby parsing and filter edge cases from review

- validate jobUrl as http(s) only; drop postings with unsafe URLs
- validate postings individually so one bad row can't empty the board
- namespace the all-filter sentinel to avoid colliding with real values
- dedupe the job metadata line (fixes duplicate React keys / Remote·Remote)
- parse filters server-side so deep-linked views don't flash unfiltered

* fix(careers): filter-aware empty state; drop inline comments

- JobGroups owns its empty copy via a filtersActive flag, so the server
  fallback and client board render identical, correct empty messaging
  (no-open-roles vs no-matching-filters)
- convert remaining inline comments to TSDoc
2026-07-01 01:25:19 -07:00
Waleed af53eda5ac feat(landing): reintroduce /contact page styled like /demo (#5315)
* feat(landing): reintroduce /contact page styled like /demo

- Restore the /contact page (removed in #5181) with a two-column layout
  mirroring /demo: value prop + trusted-by logos on the left, a message
  form card on the right, on the platform light tokens and chip components
- Restore the contact contract, /api/contact route (rate-limit, honeypot,
  Turnstile, help-inbox notification + visitor confirmation), now fully
  contract-bound via parseRequest
- Add a useSubmitContact React Query mutation hook
- Link Contact from the footer Resources column and add it to the sitemap

* fix(contact): server-authoritative captcha + review fixes

- Make captcha server-authoritative: drop the client-trusted captchaUnavailable
  flag; a valid Turnstile token is the only way past the stricter fallback
  bucket, so callers can't opt out of the challenge
- Re-execute the Turnstile widget on every submit (incl. after expiry) instead
  of falling into the no-captcha path once the token expires
- Reset the pre-submit gate on mutation settle so rapid double-clicks can't fire
  a duplicate /api/contact request
- Map only feature_request to its email type; every other topic resolves to a
  General Inquiry confirmation so support requests aren't labeled bug reports
- Drop the confirmation-email promise from the success copy (it's best-effort)
- Collapse the duplicated no-captcha rate-limit branch; hoist shared response
  constants; read the Turnstile site key as a module constant

* fix(contact): drop redundant Turnstile hostname pin

The Turnstile site key is already domain-bound in Cloudflare, so pinning
expectedHostname to the marketing SITE_URL (www.sim.ai) only rejected valid
tokens issued on self-hosted, preview, and apex-vs-www hosts. Remove the pin
and rely on Cloudflare's own domain binding.

* fix(contact): fail closed on the no-captcha rate-limit backstop

checkRateLimitDirect fails open on limiter-storage errors so a limiter outage
never takes down normal traffic. But the contact route's no-captcha bucket is
the only throttle on token-less submits, so a fail-open there let uncaptcha'd
requests reach the email path unthrottled during an outage.

- Add an opt-in { failClosed } option to checkRateLimitDirect; default behavior
  (fail open) is unchanged
- Use failClosed on the contact no-captcha backstop so an unenforceable limit
  rejects instead of admitting
- Cover both fail-open and fail-closed paths with tests

* refactor(contact): TSDoc over inline comments

Move the captcha-design rationale into the route handler's TSDoc and drop the
inline body/JSX comments, per the project's TSDoc-only comment convention.
2026-07-01 01:17:11 -07:00
Waleed 43ed80c7e3 improvement(broadcast): white canvas, LinkedIn footer, hi-res logo (#5317)
- Drop the #F8F8F8 canvas to a clean white background (body + outer wrapper).
- Replace Discord with LinkedIn in the footer social row.
- Swap the low-res header logo for the full-res sim logotype.
2026-07-01 00:53:44 -07:00
ca34301d7f fix(mailer): permissions entitlements for enabling/disabling (#5312)
* v0.6.29: login improvements, posthog telemetry (#4026)

* feat(posthog): Add tracking on mothership abort (#4023)

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

* fix(login): fix captcha headers for manual login  (#4025)

* fix(signup): fix turnstile key loading

* fix(login): fix captcha header passing

* Catch user already exists, remove login form captcha

* fix(mailer): permissions entitlements for enabling/disabling

* fix lifecycle for agentmail infra

---------

Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Theodore Li <theodoreqili@gmail.com>
Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com>
Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 21:23:11 -07:00
Vikhyath Mondreti ff16b1b886 fix(hitl): build the full enabled-block DAG so any persisted resume target exists (#5313) 2026-06-30 20:58:13 -07:00