mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
8adeaab8a56beca19becd2b5cf802679e78d9913
746
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8adeaab8a5 |
feat(gitlab): access, membership, and user-admin operations (#5710)
* feat(gitlab): add access, membership, and user-admin tools Adds member, invitation, access-request, SAML group link, and user administration tools to the GitLab integration. Resource-scoped ops work against projects or groups; user-admin ops require an admin token. All tools reuse the existing host/SSRF guard via getGitLabApiBase and add a shared getGitLabResourcePath helper. * feat(gitlab): wire access operations into the GitLab block Adds the new operations to the block dropdown and tools access list, with a named access-level dropdown (enum in, integer out), first-class expires_at, a /members/all default (direct-only opt-in), resource-type selector, and member_role_id passthrough. * test(gitlab): cover access operations Covers the access_level enum-to-integer coercion, the /members/all default vs direct-only, the 409-duplicate-add soft success, invitation per-email error handling, user-status-action response parsing, and getGitLabResourcePath. * docs(gitlab): document access and membership operations * Update apps/sim/blocks/blocks/gitlab.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * fix(gitlab): address review findings - update_user now sends admin:false so the Administrator switch can demote (an untouched switch stays undefined and leaves the flag unchanged) - expose the access-level dropdown for Update Invitation - normalize comma-separated invite emails so spaced multi-email input works * fix(gitlab): make update-invitation access level optional Update Invitation now uses a dedicated dropdown that defaults to 'Leave unchanged', so updating only the expiration no longer silently resets the invitation's access level to Developer. The level is sent only when explicitly chosen. * fix(gitlab): validation pass — SAML provider param, member/invitation query filter, moderation user guard, registry order * fix(gitlab): apply 10-agent validation findings across all 62 tools - MR draft flag now applied via Draft: title prefix (GitLab has no draft body param) - update_user only sends admin when a real boolean (untouched switch serialized null and could demote admins) - add_member 409 soft-success now verified against the conflict body - auto_merge sent alongside deprecated merge_when_pipeline_succeeds - job log capped at 200k chars, file content at 1M chars, with truncated outputs - MR diffs signal hasMore beyond 100 files - wire dropped params: update_issue milestoneId, MR milestone/squash/removeSourceBranch, pipelines ref, tree ref, branches search, commits since/until/path/author, update_file lastCommitId, jobs includeRetried, create_user forceRandomPassword - complete pipeline/job status enums, access-level enums, widen stale type unions - guards: update_invitation requires a change; create_user requires a password strategy - fix double-encoding trap in path descriptions; doc-accuracy touch-ups * fix(gitlab): review round 1 — dedicated no-default access level for update member, expiration clearing via explicit empty string * fix(gitlab): explicit Clear Expiration toggle for update member/invitation * feat(gitlab): expose full documented API surface across tools and block - membership: add-by-username, remove-member cleanup flags, list-member filters (user_ids/state/seat info), invite_source - listings: search/visibility/owned/membership, assignee/milestone filters, source/target branch filters, per-domain order-by + sort direction - CI: pipeline variables + spec:inputs, manual-job variables - repo: commit authoring (start branch, author, execute flag), release tag message + asset links, cross-fork compare + unidiff, internal notes - catalog: access-governance template + member-provisioning and access-request-audit skills - hardening from 3-agent validation: declared release params, tolerate single-object asset links, NaN guard on assignee filter, null/scalar JSON rejection * fix(gitlab): tri-state controls for update-op booleans (executable flag, MR squash/remove-source-branch) --------- 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> |
||
|
|
68c4f28b77 |
feat(clickup): webhook triggers, hierarchy selectors, and Docs knowledge-base connector (#5708)
* feat(clickup): webhook triggers with auto-managed subscriptions + hierarchy selectors * feat(clickup): KB connector (Docs v3), selector-route hardening, subblock migrations, registry-check regex fix * test(clickup): webhook provider handler tests + redact create-response secret in error logs * fix(clickup-connector): trim final page to maxDocs cap with precise listingCapped semantics * chore(clickup): lint formatting * fix(clickup): restore list-op location requiredness, split listSpaceId migration target, surface failed webhook rollback * fix(clickup): clickup.lists selector accepts listSpaceId context like clickup.folders * fix(clickup): integer-only maxDocs and location filters, depth-aware doc headings, most-specific-location hints |
||
|
|
ff1d061897 |
feat(tools): Rocketlane integration — 64 tools across projects, tasks, phases, fields, time tracking, spaces, and invoices (#5709)
* feat(rocketlane): Rocketlane integration — 64 tools across projects, tasks, phases, fields, time tracking, spaces, and invoices * fix(rocketlane): allow clearing optional fields on update, require a user reference for time-off and placeholder assignment * chore(rocketlane): trigger fresh review round * fix(rocketlane): require an owner reference when creating a project, matching the API contract |
||
|
|
d7f0a110fb |
feat(credentials): client-credentials service accounts — Zoom, Box, Salesforce + Pipedrive API tokens (#5690)
Second service-account kind (follow-up to #5682): client-credential pairs (client ID + secret + org identifier) that mint short-lived tokens at execution — in-memory cache with ciphertext-fingerprint validation (rotation-correct across instances), single-flight coalescing, 30s failure memo, no refresh-token storage. - Zoom Server-to-Server OAuth, Box Client Credentials Grant, and Salesforce client-credentials (integration user) are the first minters; Salesforce's live instance_url rides the existing instanceUrl plumbing so all 40 tools work unchanged - Pipedrive lands as a token-paste provider with explicit authStyle threading: descriptor declares x-api-token, one shared header helper drives all 18 tools + both selector routes, OAuth Bearer behavior untouched - SSRF-allowlisted Salesforce My Domain host (production/sandbox/developer partitioned domains); ENOTFOUND maps to site_not_found, EAI_AGAIN stays provider_unavailable; 408/429 from token endpoints never blamed on creds - Create route forwards clientId/clientSecret/orgId to the builder, pinned by a route-level regression test - Zoom user-scoped tools document that server-to-server tokens don't support 'me' - 4 setup-guide docs pages verified against current vendor flows (incl. Salesforce External Client Apps — the classic Connected App wizard is disabled by default since Spring '26); integrations sidebar gains a Service Accounts & API Keys section |
||
|
|
3a632936ab |
feat(clickup): ClickUp integration — 23 tools, OAuth + API-token auth, attachment upload (#5702)
* feat(clickup): add ClickUp integration with OAuth + API-token auth, 23 tools, block, and attachment upload
- 23 tools covering tasks (create/get/update/delete/list/search), comments
(create/get/update/delete), attachment upload, tags, members, custom
fields, and the workspace/space/folder/list hierarchy
- OAuth provider wiring (authorization-code flow, non-expiring tokens) plus
clickup-service-account token-paste credential (personal pk_ API tokens),
with a shared clickupAuthorizationHeader helper (pk_ tokens sent bare,
OAuth tokens as Bearer)
- File upload follows the internal-route pattern: contract-validated
/api/tools/clickup/upload-attachment builds the multipart form and
returns UserFiles
- ClickUp block with per-operation subBlocks, canonical file param,
BlockMeta templates/skills, and gradient brand icon
- Generated integration docs page + hand-written service-account guide
* fix(clickup): apply validation-audit fixes across tools, block, and upload route
- Map documented task fields that were dropped: markdown_description,
subtasks, watchers, custom_fields, time_spent, folder, space — making
the include_subtasks / include_markdown_description options observable
- Expand verified filters: assignees/tags/due-date ranges on get_tasks and
search_tasks, include_closed on search_tasks; add due_date_time /
start_date_time flags and update-task assignee add/remove
- Guard update_comment against an empty body and require comment text in
the block; prefer markdown_content over content on create_list and make
markdown reachable for lists in the UI
- Drop the unverified 'required' field from custom-field outputs; read
both err and error keys from ClickUp error bodies; correct notify_all
wording
- Upload route: 100MB size cap, shared attachment mapper with full
documented response fields (version, thumbnails), base-URL constant
* fix(docs): restore clickup-service-account guide and shield it from doc generation
The generator prunes integration pages it does not derive from blocks;
add the hand-written ClickUp API-token guide to
HANDWRITTEN_INTEGRATION_DOCS so regeneration cannot delete it.
* fix(clickup): address review findings — dedupe catalog entries, config-time list parent validation, upload memory cap, unique icon gradient ids
- Remove duplicated clickup entries in docs meta.json and integrations.json
introduced by a double docs regeneration
- Add a Location dropdown for Get Lists / Create List so the folder ID or
space ID is conditionally required at configuration time instead of
failing at run time
- Pass the 100MB cap into downloadServableFileFromStorage so oversized
files abort during download instead of after full buffering
- Use useId()-derived SVG gradient ids for ClickUpIcon in both icon files
* chore(clickup): format integrations.json entry per biome
* improvement(clickup): final validation-pass refinements across tools and block
- create_task: add doc-backed sprint points param (parity with update)
- get_tasks/search_tasks: expose include_markdown_description
- update_task legacy numeric priority in list responses mapped instead of
dropped; create_comment omits absent response fields instead of
emitting sentinel ''/0 values
- order_by only sent when explicitly chosen (Default sentinel); comment
text no longer UI-required for update_comment (resolve-only and
assignee-only updates are valid per the tool contract, which still
rejects an empty body)
- add_tag_to_task sends no request body per docs; upload tool tolerates
non-JSON error responses
* fix(clickup): tolerate nested user wrapper in member mapping
The task/list member endpoints document a flat member object; accept the
workspace-members-style nested { user: {...} } wrapper as well so both
shapes map correctly.
* fix(clickup): map size-limit errors from download/compile to a 400 upload-size response
downloadServableFileFromStorage enforces maxBytes on both the raw download
and the resolved (compiled) artifact via PayloadSizeLimitError; catch it in
the route so oversized content returns the intended 400 instead of
bubbling to the generic 500 handler.
* feat(clickup): add custom field values, checklists, and time tracking (15 tools, 38 total)
- Set/remove custom field values on tasks (PUT/DELETE /task/{id}/field/{field_id});
block value input parses JSON for structured field types, plain values pass through
- Checklist CRUD: create/rename/reorder/delete checklists and create/update/
delete checklist items (assign, resolve, nest), mapped from the documented
{checklist} response shape
- Time tracking: list entries in a date range (assignee/location filters,
task-tag and location-name includes), create/update/delete entries, start/
stop timers, and read the currently running timer; entries mapped from the
documented data envelope with negative-duration running semantics
- Block gains 15 operations with conditionally-required fields, timestamp
wand configs, tri-state billable/resolved dropdowns, and a single-location
filter selector matching the API's one-location-filter rule
* fix(clickup): new-tools audit fixes — POST for set custom field value, tolerant time-entry envelopes, richer mappings
- Set Custom Field Value uses POST per the live reference OpenAPI (the
llms mirror shows PUT; the reference console spec is authoritative)
- delete_time_entry maps the documented array envelope; create_time_entry
tolerates both data-wrapped and flat echo bodies
- Time entries surface task_tags and task_location so the include switches
are observable; checklists carry date_created
- Custom field value input parses any JSON literal (numbers, booleans,
arrays, objects) and passes plain text through
- Update Time Entry supports duration edits; single-assignee time ops get
their own field so a comma-separated list can't silently NaN out
* fix(clickup): send explicit date-time flags whenever a date is set
The due/start date-time switches previously only transmitted true; a
timed date could never be flipped back to date-only. The flag is now sent
as an explicit boolean whenever the corresponding date is provided and
omitted otherwise.
* improvement(clickup): final per-tool audit polish — checklist item children, tolerant comment date
- Checklist items surface the documented children array of nested item IDs
- create_comment tolerates a string-typed date in the response
* fix(clickup): reject empty update_task bodies with a clear local error, matching sibling update tools
|
||
|
|
54b35a4f0e |
improvement(deployments): bugfixes for run-block, airtable + external sub management (#5680)
* improvement(webhooks): external subscription management
* ui/ux
* remove test file
* fix tests
* address comments
* address comments
* update to grain v2 api
* improvement(grain): hide auto-registered webhook URL on v2 triggers
* Revert "improvement(grain): hide auto-registered webhook URL on v2 triggers"
This reverts commit
|
||
|
|
e2ea49ea7e |
feat(instagram): add Instagram integration (#5568)
* feat(instagram): add Instagram Login OAuth, tools, and block * feat(instagram): add Gmail-style media uploads for publish ops Resolve UserFiles to Meta-fetchable presigned HTTPS URLs (600s TTL) via internal publish routes, and fix OAuth scope storage plus connect-draft wiring so Instagram Login publishing is testable end-to-end. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(instagram): simplify messaging tools to direct requests, clean up types * fix(instagram): parallelize carousel child polling, enforce 2-10 items, extend poll window and insights periods, use canonical user_id in OAuth callback * fix(instagram): resolve user id from user_id only, accept numeric user_id Co-authored-by: Cursor <cursoragent@cursor.com> * fix(instagram): use form/query params for publish and comment endpoints, request message timestamps explicitly Co-authored-by: Cursor <cursoragent@cursor.com> * fix(instagram): normalize Graph ID outputs to strings so downstream .trim() calls are safe Co-authored-by: Cursor <cursoragent@cursor.com> * style(instagram): use brand gradient tile for the block icon Match the official Instagram look by filling the tile with the orange–pink–purple radial gradient so the white camera glyph sits on a full-bleed brand background. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(instagram): tighten publish defaults and cloud-storage upload UX Default Reel share-to-feed to Yes, drop unused media fields params, share publish transform helpers, and warn when cloud storage is missing for Meta-fetchable uploads. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(instagram): fail closed when cloud storage status is unknown Treat loading/error as blocked for requiresCloudStorage uploads, show the warning once the check finishes, and disable selecting local workspace files Meta cannot fetch. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(instagram): proactively refresh long-lived tokens before expiry Meta only allows refreshing still-valid Instagram tokens, so refresh within 14 days of expiry (after the 24h age gate) instead of waiting until after accessTokenExpiresAt. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(oauth): restore TikTok clientIdParamName JSDoc after merge The staging merge dropped the opening /** on ProviderAuthConfig.clientIdParamName, which broke TypeScript parse in CI. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(api): Zod-contract storage-status and ratchet validation baseline Wire /api/files/storage-status through a shared route contract so the strict API validation audit stays at zero non-Zod routes after the new Instagram cloud-storage check. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(instagram): match Gmail advanced media placeholders Drop public-URL paste hints from advanced fields and the cloud-storage banner so the UI mirrors Gmail attachments. Co-authored-by: Cursor <cursoragent@cursor.com> * code review + hide from toolbar * address comments * fix(instagram): drop hidden Instagram from OAuth catalog pin test Instagram is hideFromToolbar so it is excluded from integrations.json; the pinned slug map must not expect it until the block is visible again. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(instagram): validate client ID before creating connect draft Avoid orphan pending credential drafts when INSTAGRAM_CLIENT_ID is missing, matching the Shopify authorize ordering. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai> |
||
|
|
00a2f26fe5 |
feat(credentials): token-paste service accounts for 12 providers (#5682)
* feat(credentials): token-paste service accounts for 12 providers (HubSpot, Airtable, Notion, Asana, Attio, Linear, monday, Shopify, Webflow, Trello, Cal.com, Wealthbox)
* refactor(credentials): registry-dispatch service accounts + doc-verified validator hardening
- Migrate Google/Atlassian/Slack service-account branches to the same
registry pattern as token-paste providers (builder + resolver maps,
unified required-fields contract validation)
- Fix Shopify service-account store domain never reaching tool URL builders
- Linear: rate-limit 400s no longer mislabeled as invalid credentials;
validation header matches runtime header rule
- Trello: server API key rejection no longer blamed on customer token
- Fleet-wide: network errors and non-JSON provider bodies map to
provider_unavailable via shared fetchProvider/parseProviderJson
- HubSpot: drop regional-host displayName upgrade; Wealthbox: explicit 402;
monday: provider-side GraphQL errors mapped correctly; Attio: null-body guard
* polish(credentials): ship-gate fixes, setup-guide docs, and lint pass for token service accounts
- 12 setup-guide docs pages (docs.sim.ai/integrations/<provider>-service-account)
incl. the Trello authorize-link flow the integration depends on
- Attio: HTTP 400 on /v2/self maps to invalid_credentials (live-verified)
- monday: scan all GraphQL errors for provider-side codes; no empty audit ids
- Compile-time descriptor/validator lockstep (typed registry) and
prototype-safe provider-id guard (Object.hasOwn)
- Shared errors.test.ts pinning fetchProvider/parseProviderJson/
throwForProviderResponse guarantees for all validators
- Stale TSDoc updated after the registry migration; biome organizeImports pass
* docs(credentials): vendor-doc accuracy pass on service-account setup guides
34 corrections from a 12-agent audit against live official vendor docs:
current HubSpot Development-area nav, Notion connections rename, monday
Developer Center paths, Shopify legacy-vs-Dev-Dashboard token flows, and
hedged wording for claims vendors do not document (expiry, limits)
* improvement(credentials): vendor-accurate credential nouns on connect surfaces
'Add service account' only where the vendor actually has service accounts
(Google, Atlassian); token-paste providers now use their own vocabulary via a
connectNoun descriptor field — 'Add private app token' (HubSpot), 'Add API key'
(Attio/Linear/Cal.com), 'Add personal access token' (Airtable), etc. Docs page
titles updated to match; slugs and internal provider ids unchanged
* fix(credentials): classify auth-shaped Shopify GraphQL errors as invalid credentials
Shopify can reject invalid or revoked shpat_ tokens with HTTP 200 and a
GraphQL error body instead of a 401; those now map to invalid_credentials
instead of a provider-outage message
* fix(credentials): empirically-grounded HubSpot token verification
Live probing showed the documented access-token-info route returns a bare
404 for unrecognized tokens (ambiguous with a missing route), so 404/400
now falls back to the Account Information API, which answers with a JSON
401 for rejected tokens and 200/403 for live ones — verified against the
real endpoints
* fix(credentials): review-round-2 fixes for service-account edge cases
- Shopify tools prefer the credential-validated store domain over the
block's auto-detected shopDomain (a store-bound token must hit its own store)
- Unknown non-empty service-account providerIds are rejected instead of
silently persisting as google-service-account (empty stays the legacy
Google fallback)
- Shopify/modal domain normalization strips URL paths ('https://x.myshopify.com/admin')
- monday: warning-class GraphQL errors no longer reject a token whose me
data proves it authenticated
* chore(credentials): format shopify validator test
* fix(credentials): cold-review hardening pass
- Object.hasOwn guards on all provider-id registry lookups (crafted
'__proto__'/'constructor' providerIds now 400 instead of 500) + regression test
- fetchProvider gets a 10s AbortSignal.timeout so a hung provider can't pin
the create/reconnect request
- HubSpot: unexpected 403 on the token-info route defers to the account-info
fallback instead of blaming the token
- Linear selector routes use the SDK apiKey option for lin_api_ keys (bare
header parity with the tools sweep)
- Docs: UI steps aligned to the vendor-noun connect labels; HubSpot
scope-propagation claims softened; Trello in-product-link promise corrected
|
||
|
|
2e5b33c2db | feat(community): replace Discord community links with Slack across app, docs, emails, and readme (#5653) | ||
|
|
e7afe45b2d |
improvement(buffer): explicit mediaType override instead of guessing on ambiguous media (#5642)
* improvement(buffer): explicit mediaType override; error instead of guessing on ambiguous media * fix(buffer): forward validated mediaType from routes into the media resolver * fix(buffer): presign uploaded media for 7 days — Buffer fetches assets at publish time * docs(buffer): document presign lifetime bound by signing credentials * fix(buffer): always mint fresh presigned URLs from verified storage keys for media |
||
|
|
2d41360807 |
improvement(concurrency): limits configurable, docs updates (#5640)
* improvement(concurrency): limits configurable, docs updates * remove dead tests * limits self hosted vars |
||
|
|
e94e514c0a |
feat(flint): add Flint integration with agent task tools, block, and docs (#5641)
* feat(flint): add Flint integration with agent task tools, block, and docs * fix(flint): forward explicit publish=false, guard missing taskId, align default params branch with tool fallback * docs(flint): add manual intro section to integration docs page * fix(flint): fail get_task on OK responses without a task ID * fix(flint): drop json-object generation type so the wand emits the pages array |
||
|
|
04535ee33b |
feat(buffer): add Buffer integration with posts, channels, and ideas (#5637)
* feat(buffer): add Buffer integration with posts, channels, and ideas * fix(buffer): return clear tool error when account lookup yields no account * fix(buffer): default schedulingType server-side so basic-mode blocks never fail validation * fix(buffer): probe Content-Type for extensionless media URLs so videos are not sent as images * feat(buffer): add get_ideas and get_idea_groups tools, harden media URL classification * fix(buffer): guard missing post on PostActionSuccess responses |
||
|
|
b1bd2b5cab |
feat(gong): align tools with official API spec + 4 new tools (#5632)
* feat(gong): align tools with official API spec, add ask-anything, brief, unassign, and logs tools
- fix gong_list_flows: query param was flowEmailOwner (typo copied from Gong's endpoint prose); the API requires flowOwnerEmail, so every call failed
- create_call: drop phantom url output (API returns only requestId/callId) and make downloadMediaUrl optional per spec
- list_scorecards: refresh to current spec (numeric IDs, questionType/answerGuide/minRange/maxRange/answerOptions, reviewMethod)
- answered_scorecards: map selectedOptions on answers, correct score range description (1-50)
- surface requestId uniformly across all read tools; totalRecords no longer fabricated from page size
- normalize includeAvatars to a strict boolean param, uppercase aggregationPeriod, encode userId path param
- validate email/phone format before irreversible GDPR purge calls
- new tools: gong_ask_anything, gong_get_brief (AI entity Q&A/briefs), gong_unassign_flow_prospects, gong_get_logs
* fix(gong): require custom-range dates and gate param remapping by operation
- ask_anything/get_brief: entityFromDateTime/entityToDateTime now conditionally required (UI) and validated tool-side when timePeriod is CUSTOM_RANGE
- block params mapper: remaps are gated by the selected operation so stale values from previously configured operations can no longer overwrite fromDateTime/fromDate/workspaceId
* fix(gong): final spec-alignment pass, review fixes, and regenerated docs
- ask_anything/get_brief: send fromDateTime/toDateTime only for CUSTOM_RANGE; declare mcpResult brief section field
- unassign: dedicated optional unassignFlowId subblock so a stale assign-flow ID can never silently narrow an unassign to one flow
- get_logs: logType is a closed enum (AccessLog, UserActivityLog, UserCallPlay, ExternallySharedCallAccess, ExternallySharedCallPlay) - dropdown in block, enumerated in tool description
- get_folder_content: folderId optional per spec; get_call: encode callId path param
- list_trackers: drop saidInCallParts (absent from the spec's KeywordTracker schema)
- get_extensive_calls: correct declared interactionStats item shape to {name, value}
- block inputs: list all remapped subblock params; optional flags on nullable outputs; absolute types re-export
- regenerate Gong integration docs and integrations.json entries (29 operations)
|
||
|
|
fc25cfb3c8 |
fix(docs): fix Core Web Vitals regressions on docs.sim.ai (#5630)
* fix(docs): fix Core Web Vitals regressions on docs.sim.ai Empirically measured under real trace-based (devtools) CPU/network throttling against the live site: mobile Performance 59, LCP 9.2s (TTFB 745ms + 8.4s element render delay). - sidebar-components.tsx / [lang]/layout.tsx: the docs sidebar renders every page in the doc tree as a link at once. Next's default viewport-prefetch fired an RSC payload fetch for every one of them on initial load - dozens of concurrent requests competing with the page's own content for bandwidth. Wired fumadocs' documented `sidebar.prefetch` option through to the custom SidebarItem/SidebarFolder components (which were bypassing it entirely, using next/link directly with no prefetch prop) via the `useSidebar()` context hook. - video.tsx: `autoPlay` forces browsers to fetch the full video file immediately on mount regardless of `preload`. Gated actual src loading behind an IntersectionObserver so a page with several of these doesn't pull down every video up front (5MB across 3 requests, in this case). Single shared component - fixes every doc page that embeds one. - proxy.ts: the i18n middleware matcher excluded favicon/robots.txt/etc but not `icon.svg`, so every request for it got routed through i18n negotiation instead of served as a static file, 404ing in production. - next.config.ts: enable productionBrowserSourceMaps - safe since this repo's source is already fully public, real debuggability benefit, zero performance cost. - shiki 4.0.0 -> 4.3.1 (verified: syntax highlighting still renders correctly). Attempted a coordinated fumadocs-core/ui/mdx/openapi upgrade to latest; fumadocs-openapi's v11 factory function became client-only (breaking change beyond its declared peer deps, requiring a component-boundary restructure), so only the safe, verified, docs-exclusive bumps (fumadocs-core/ui/mdx, shiki) are included here - the openapi major bump needs its own dedicated migration PR. Verified via a real production build (dummy env, all 3974 pages including API reference render/build cleanly) and a clean (non-stale) local server: Performance 59 -> 71 measured under real devtools throttling, RSC prefetch requests 63 -> 11, video requests/bytes 3/5MB -> 0. A pre-existing React hydration warning (#418) was found and confirmed present on live production before any of these changes, unrelated to this diff - documented, not blocking. * fix(docs): fall back to eager video loading without IntersectionObserver The lazy-load gate from the previous commit threw before isInView could ever become true in environments lacking IntersectionObserver (older browsers, some embedded webviews), leaving videos permanently source-less instead of falling back to eager loading. * chore(docs): drop non-TSDoc inline comments Repo convention is TSDoc-only, no plain // comments. * fix(docs): accessibility and SEO defects across the docs app Audited with parallel subagents against the accessibility and SEO skill checklists, each fix verified by reading the actual code (not assumed): Accessibility: - lightbox.tsx: focus was never captured/restored on close, and Tab escaped the modal to the page behind it (no focus trap on the single focusable element) - heading.tsx: the per-heading copy-link icon only appeared on hover, invisible to keyboard-only navigation (added peer-focus-visible) - navbar.tsx: active nav tab had no aria-current - response-section.tsx: the status-code dropdown had no aria-haspopup/aria-expanded/role, and no Escape-to-close - workflow-preview.tsx: same focus-trap gap as lightbox.tsx on the expanded-canvas modal SEO: - page.tsx: generateMetadata's hreflang/canonical URLs used a naive String.replace to strip the locale prefix, which also matched "/en" inside unrelated slugs (platform/enterprise, integrations/enrich, platform/self-hosting/environment-variables), corrupting those pages' canonical and alternate-language URLs. Replaced with a prefix-only strip. - structured-data.tsx: the SoftwareApplication JSON-LD block compared url === baseUrl (no trailing slash) against the homepage's actual url (always has a trailing slash), so the condition was always false and this structured data never rendered anywhere, including the homepage. - structured-data.tsx: "Mothership" in the indexed featureList violated the constitution's required language (the agent is "Sim", the surface is "Chat") - this ships in JSON-LD search engines parse. * fix(docs): defer the Ask Sim chat widget's heavy deps until opened The chat panel (useChat from @ai-sdk/react, Streamdown + its CSS) was mounted unconditionally in the root layout on every single page, so its full weight loaded and executed even though the widget starts closed on every page view. Traced via the LCP breakdown insight under real devtools CPU/network throttling: the LCP text element (the intro paragraph) had a ~8s element render delay despite a ~13ms TTFB, and bootup-time attributed ~4.3s of scripting time to a single chunk containing React/ReactDOM's own runtime plus this widget's eagerly-bundled dependencies. Split into a lightweight ask-ai.tsx (just the toggle button + open state) and ask-ai-panel.tsx (the actual chat UI, useChat, Streamdown), loaded via next/dynamic(..., { ssr: false }) only when the user opens the widget. Verified: the panel's chunk now has zero network requests on initial page load. Measured (mobile, devtools throttling, /introduction): - Performance: 69 -> 75 - LCP: 8.0s -> 6.4s - TBT: 260ms -> 130ms The remaining ~6.4s LCP delay traces to the same shared chunk, now identified as core React/ReactDOM hydration cost for this page's sidebar/TOC/breadcrumb tree rather than an isolated bug - a real, larger initiative (hydration architecture, not a surgical fix), documented here rather than rushed. * fix(docs): preserve Ask Sim chat state across close/reopen The panel split unmounted AskAIPanel entirely on close, discarding useChat's message state - reopening always started an empty conversation, unlike the original single-component layout where useChat lived in a component that never unmounted. Fixed by keeping the panel mounted (via a hasOpened flag that never resets) once first opened, and having the panel itself return null when closed rather than being conditionally removed from the tree by its parent - hooks still run every render, so useChat's state persists across visibility toggles. The dynamic import still only fires on the first open, so the initial-load win is unchanged. Verified via a real click-through (open, type, close, reopen): input persists correctly, and the panel chunk still has zero network requests on initial page load. Performance unchanged at 75. * chore(docs): lint fixes (import order, formatting) * fix(docs): fill the Ask Sim UI gap while the panel chunk loads handleOpen set open=true synchronously, hiding the trigger button before the dynamically imported panel had a chance to render anything (next/dynamic renders null by default with no loading option) - on a slow connection neither the button nor the panel was visible. Added a loading fallback in the same fixed position so there's no gap between the button disappearing and the real panel appearing. |
||
|
|
ef7c8e24b2 |
feat(platform): settings permissions, admin, billing attribution (#5545)
* fix(invites): preserve active organization for external access Keep organization activation server-owned so failed membership checks cannot clear a valid session context. * feat(admin, billing, settings): cleanup settings visibility, billing actor resolution, new admin routes * address comments * chore(db): reset pending migrations before staging merge Remove locally generated migrations so they can be regenerated against the latest staging schema without preserving stale snapshots or numbering. * regen migrations * address comments * chore(db): reset generated migrations before staging merge Remove this branch's generated migrations so they can be regenerated against the latest staging schema with fresh numbering. * upgrade global work * fix lint * address comments * legacy callbacks correctness * address comments * update * guardrail attribution |
||
|
|
fcf4e02930 |
fix(landing): repair Lighthouse-flagged CWV audits on production (#5605)
* fix(landing): repair Lighthouse-flagged CWV audits on production
Empirically verified against a live full Lighthouse run of www.sim.ai
(production, pre-fix) plus a local build of the exact deployed commit with
source maps temporarily enabled for root-causing. Distinguished genuinely
failing audits from passing ones already misread as broken.
- fetchPriority missing on every LCP hero image: `priority` generates a
preload <link> but Next does not auto-add fetchpriority=high to it -
confirmed via raw deployed HTML diff. Added explicit fetchPriority='high'
to all 5 priority Image usages (hero, enterprise, blog/library post +
index cards).
- valid-source-maps failing: production ships no source maps at all
(productionBrowserSourceMaps defaults false). Enabled it - safe here since
this repo's frontend is already fully open source, so no incremental
exposure versus Next's default.
- image-delivery-insight (55.8KB wasted): feature-integrate-ui.png's `sizes`
hint was a flat 1050px regardless of viewport, so mobile fetched the
1920w variant for a ~423px real render. Replaced with a responsive sizes
expression derived from the sibling backdrop image's own (already
correct) hint, scaled by the callout's documented 125% overhang.
- cache-insight (best-fixable portion): _next/static/* filenames are
content-hashed and immutable per deploy, but shared one cache rule with
unhashed /public assets, capping both at 1-day max-age. Split into two
rules - hashed assets now get 1-year immutable, unhashed assets keep the
shorter revalidating TTL. Verified via a real build + server that both
paths now return the correct distinct header.
Investigated and NOT changed (documented, not assumed):
- legacy-javascript-insight (14KB): traced via sourcemap to
next/dist/build/polyfills/polyfill-module.js - Next's own built-in
polyfill bundle, not our code or a dependency, and not exposed via any
next.config.ts option. No browserslist misconfiguration on our end (none
exists; Next already defaults to its modern target).
- forced-reflow-insight: even with source maps present locally, the
dominant cost (335-417ms) stayed [unattributed] by Chrome's own profiler,
and the small attributed slice was non-deterministic between our own
chunk and a third-party script (HubSpot analytics) across runs - not a
confident single root cause worth a targeted fix.
- render-blocking-insight / network-dependency-tree / bf-cache: bf-cache's
actual failure reason is Cache-Control: no-store on the main document -
the exact root cause already fixed on staging (PR #5522/#5528, the
PublicEnvScript/unstable_noStore fix) but not yet promoted to main/prod.
Resolves once that ships, not additional work here.
* fix(landing): convert mothership cover from PNG to JPEG (/blog LCP 6.6s -> 2.8s)
Ran a full Lighthouse sweep across every public page as requested. /blog
scored 73 (LCP 6.6s) while every other page scored 95+ - reproduced
consistently across 3 runs, not noise. Traced via lcp-breakdown-insight:
the LCP image (mothership/cover.png, 241KB even after the earlier palette
compression pass) took 6+ seconds to download on simulated mobile
throttling, well beyond what its size should cost.
PNG is a poor fit for this illustration's subtle gradients versus JPEG's
lossy compression. Verified empirically before converting: same 1920x1080
resolution, visually identical (spot-checked), 241KB -> 65KB (73% smaller).
No other cover in the content set uses PNG and benefits the same way
(checked copilot/cover.png, the only other PNG cover - already optimal at
64KB, converting it yielded no improvement, left unchanged).
Verified fix: /blog score 73->93, LCP 6.6s->2.8s, reproduced across 3 runs.
* fix(landing): correct mobile sizes tier, drop non-functional cache rule
- integrations-callout: account for FeatureCard's max-lg:grid-cols-1 mobile
stack in the sizes hint, verified against Lighthouse's measured mobile
render width.
- next.config: remove a custom _next/static cache-control rule that never
actually fired (confirmed via header-marker test) - Next's own built-in
default already applies the correct immutable 1yr cache to that path.
* fix(landing): correct sizes underestimate + fix dead .map header rule
- integrations-callout: derive sizes from the section's actual grid math
(fixed 386px copy column, 40px gap, section gutters) instead of an
approximated vw fraction. Verified against a static reproduction of the
layout rendered at each Tailwind breakpoint - the old 110vw mobile tier
underestimated real render width by ~3% right at the 1023px stack
boundary, which could cause the browser to pick a too-small srcset
candidate and upscale.
- next.config: the .map header rule's trailing `$` was read as a literal
character by Next's path-to-regexp source matcher, not a regex anchor,
so the rule never matched a real .map URL (confirmed via routes-manifest
regex + a live header check). Removed the dead anchor and added a
bounded Cache-Control so a future decision to stop shipping source maps
isn't undermined by a 1yr immutable cache on already-fetched maps.
* fix(llms): serve well-formed llms.txt, remove Mothership + dead static files
Both the marketing site and docs site's llms.txt validator errors ("does
not appear to contain any links") traced to the same root cause: a static
public/llms.txt shadowed a better-written, already-existing dynamic
app/llms.txt route, and every "link" in the static files (and in the
docs app's auto-generated route) was bare `label: url` text, not Markdown
link syntax - so a strict Markdown-link parser found zero matches even
though URLs were visibly present.
- apps/sim: delete public/llms.txt (dead code, shadowing the properly
Markdown-linked app/llms.txt route.ts, confirmed via production headers
showing the static file was what actually served). Fix llms-full.txt's
Links/Support/Legal sections to use [label](url) syntax, correct a
stale "Next.js 15" reference, and replace "Mothership" with "Chat" per
the constitution's language rules.
- apps/docs: same shadowing issue - delete the orphaned public/llms.txt
(also still said "Mothership"). Fix the auto-generated per-page link
list in app/llms.txt/route.ts to emit [title](url) instead of
"title: url" for every documentation page.
* fix(llms): actually include the route.ts fixes from the prior commit
The prior commit (
|
||
|
|
def2d5299a |
fix(docs-og-image): match reference cover template typography exactly (#5598)
* fix(docs-og-image): match reference cover template typography exactly - swap Season Sans for Söhne Kräftig (500) — the reference cover's actual brand font, confirmed by letterform comparison; recovered from git history since it was removed as an unused static asset - fix ink/background colors to exact reference hex values - square caps + miter join on the corner arrow to match the reference's sharp corners instead of rounded ones - recalibrate title font size, line height, and wrap width for the new font's metrics * fix(docs-og-image): estimate CJK glyph width separately to avoid under-wrap wrapTitleLines budgeted a flat 0.42em/char, tuned for Latin text. Docs ships ja/zh locales — CJK glyphs render near-square (~1em), so a CJK title could overflow the fixed-width title box uncaught. Sum per-char em-width with a CJK-range check instead of counting characters. * fix(docs-og-image): fall back to character-level wrap for oversized CJK words wrapTitleLines only splits at spaces, so a space-free CJK run (common for Chinese titles) still arrived as a single word wider than the title box and rendered as one overflowing line. Falls back to character-level chunking for any word that alone exceeds maxWidthEm. |
||
|
|
f3582ed197 |
feat(branding): sim wordmark favicon/OG, docs footer parity, footer peel (#5587)
* feat(branding): sim wordmark favicon/OG, docs footer parity, footer peel - replace apps/sim favicon and default OG image with the sim wordmark logo (OG image widened, logo kept at native size) - swap the docs navbar logo to the icon-only mark (no wordmark text) - add a scroll "peel" reveal effect to the landing footer using a sticky-positioned illustration, pure CSS, no scroll listeners - port the same footer (link directory + peel effect) to the docs app so both apps are visually consistent; add Academy to Resources - rebuild the docs OG image template to match the site's existing blog/library cover style (wordmark top-left, arrow top-right, title bottom-left), working around a Satori text-measurement bug that doubled the gap after certain words * fix(docs): correct OG font, mobile logo, and footer stacking - switch the docs OG image title font from Geist to the site's real brand font (Season Sans), instantiated as a static TTF weight since Satori can't parse WOFF2 or variable fonts; served from /static/ so the i18n proxy's matcher (which excludes static but not fonts) doesn't intercept it - fix DocsLayout's nav.title (fumadocs' own mobile menu slot) to show the wordmark instead of the icon mark - add an isolated stacking context + higher z-index to both the docs and sim app footers so fumadocs' sticky z-20 sidebar can't paint over the footer content or the peel reveal * fix(docs): match OG template exactly, fix gradient/origin bugs - recalibrate the OG image to the reference cover template's actual measured values: 1200x675 canvas (was 630), ~26px margins (was 56-64px), ink #525252 (was #3f3f3f), larger wordmark/arrow/title sizing — confirmed by direct pixel measurement of the reference cover.jpg, not estimation - fix SimLogoIcon/SimLogoFull's SVG gradient ids to be unique via useId() instead of a fixed string, so multiple instances on one page don't collide (Greptile P2) - fix SIM_SITE_URL to be a hardcoded sim.ai constant instead of deriving from NEXT_PUBLIC_APP_URL, which reflects wherever this deployment runs, not the fixed public marketing site (Greptile P1) * fix(docs): route Jira footer link to the docs guide, not sim.ai Every other integration in the footer's Integrations column links to its own docs.sim.ai guide; Jira was the only one pointing at the marketing site's landing page instead, despite docs having its own /integrations/jira guide. Matches the established pattern. * fix(docs): fix sidebar-divider grid regression, footer-peel path/positioning, OG sizing, and prune stray comments - #nd-docs-layout::before divider now spans the full grid explicitly (grid-row/grid-column: 1 / -1) instead of being auto-placed into a real content cell, which was pushing page content down - footer-peel.jpg moved under /static/landing/ (was 404ing behind the i18n proxy's non-static path matcher) and wrapped in a relative div so next/image's fill positioning is valid under the sticky container - OG route: corrected title font sizes and char-width ratio so long titles wrap to 2 lines instead of 3, and resized the corner arrow to match the reference cover template's proportions - swapped the icon-only desktop navbar logo back to the wordmark - removed stray non-TSDoc comments, folded into TSDoc where the explanation was worth keeping * fix(footer): remove sticky peel reveal, keep clean footer link directory The peel's "reveal window" relied on position: sticky bottom-detaching into a containing block whose extra height came from padding-bottom — that combination doesn't reliably work in WebKit/Safari (sticky never gets room to engage when the surplus height is padding rather than an explicit height or content), so the peel stayed permanently covered by the footer regardless of viewport size. Rather than carry that unreliable technique further, removing it entirely from both apps and reverting to the plain footer link directory. |
||
|
|
8e7e2db35e |
feat(docs): update favicon, fix icon contrast, add integration intros (#5581)
* feat(docs): update favicon, fix icon contrast, add integration intros - replace docs favicon/icon assets with new sim logo - fix light-tile icon contrast in BlockInfoCard so icons like Daytona no longer render invisible (white-on-white); matches sim toolbar's brightness-based contrast logic - add missing MANUAL-CONTENT-START:intro sections to 30 integration docs pages that lacked context/links * chore(docs): normalize spacing from generate-docs pass Ran the docs generator to verify our new intro sections survive regeneration cleanly. It reformats the blank line before "## Usage Instructions" to match every other manually-annotated page. * fix(context-dev): remove prefetch and simplified-brand tools - remove context_dev_prefetch_domain, context_dev_prefetch_by_email, and context_dev_get_brand_simplified tools and their block operation entries; these aren't meant for general use - regenerate docs to drop their sections from context_dev.mdx |
||
|
|
f4d47ed826 |
feat(slack): reusable custom bot credentials, slack_v2 block (preview), redesigned trigger (#5323)
* feat(slack): enable assistant-agent tools via assistant:write scope Add assistant:write, app_mentions:read, and im:history to the Slack bot OAuth scopes so the Set Assistant Status / Title / Suggested Prompts tools (assistant.threads.*) work with users' existing Slack credentials — no new app or credentials required. Restore the action_assistant trigger capability (scope assistant:write) in the manifest generator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WpeT8J5yVCrrNQB9Hzm9uS * Add slack trigger * fix channel picker in slack trigger * improvement(slack-trigger): reorder app type, gate account to sim mode, add channel-id input * fix(slack-trigger): drop unmapped events from filter, resolve oauth token for reaction text + file downloads * fix(slack-trigger): empty operation selection fires nothing; resolve token via credential owner not execution actor * fix(slack-trigger): ignore message edit/delete/system subtypes; prefer channel picker over stale manual ids * feat(slack-trigger): single-event model with contextual filters and full event catalog * fix(slack-trigger): apply event/channel/bot filters on custom-app path too * fix(slack-trigger): don't drop edit/delete events when channel_type is absent * feat(slack): reusable custom bot credentials, slack_v2 block, interactivity triggers - Custom bot as a workspace service-account credential (set up once, shared ingest URL /api/webhooks/slack/custom/{credentialId}, reused across triggers and actions) - slack_v2 action block: credential-based Custom Bot auth alongside Sim OAuth; v1 hidden from toolbar - Interaction triggers (block_actions / view_submission) with optional action/callback id filter; settings.interactivity in generated manifests - Setup wizard: name + description, full permissions by default with ChipDropdown customization; reconnect mode rotates secrets in place - Centralized service-account token resolution (unknown provider fails loudly) - Shared Slack webhook fan-out dispatcher for native + custom ingest routes * chore(api-validation): bump route baseline to 924 after staging merge * feat(slack): preview-gate slack_v2 and the custom-bot credential surfaces slack_v2 (block + hosted slack_oauth trigger) ships preview: true — hidden from all discovery until revealed via block-visibility AppConfig or PREVIEW_BLOCKS. v1 stays toolbar-visible with the legacy slack_webhook trigger until v2 GAs. The integrations-page custom-bot setup surface rides the same flag via isHiddenUnder(slack_v2); placed instances, existing credentials, and ingest/execution paths are never gated. * fix(slack): v1 keeps slack_webhook trigger subblocks; handle object-form event channels - v1 spread had been swapped to slack_oauth's trigger subblocks (shared with v2), leaving its slack_webhook deploy path without signing-secret config (Bugbot high). v1 now carries the legacy trigger set again; v2 swaps them for slack_oauth's. - resolveSlackEventChannel reads channel.id for channel_created/channel_rename payloads, so channel filters no longer drop every rename event. * fix(slack): default absent appType to custom at deploy; deactivate custom-bot webhooks on credential delete - appType is hidden and seeded 'custom' by value(), which only covers editor-created blocks; defaultValue now persists it via buildProviderConfig and the deploy fallback flips to custom (the only exposed mode this ship) - deleting a slack-custom-bot credential now also deactivates provider='slack' webhooks routed by that credential id, not just native slack_app rows * fix(slack): resolve credential owner for deploy-time team_id lookup A teammate deploying a trigger wired to a shared Slack credential isn't the credential owner; refreshAccessTokenIfNeeded only loads tokens for the owning user. Resolve the account owner first, mirroring the runtime formatInput path. * chore(slack): reconcile staging merge - nullable webhook.path coalesced at correlation/payload/tiktok boundaries - slack dispatch delegates to staging's dispatchResolvedWebhookTarget (shared preprocess/deployment/filter/enqueue lifecycle), keeping the skip-reason diagnostics; route tests reworked around that seam - api-validation route baseline 924 -> 926 * fix(slack): workspace-scope bot credentials at deploy; recreate webhooks on routing transitions - a bot credential id is semi-public (embedded in Slack Request URLs), so the custom deploy branch now rejects credentials outside the workflow's workspace - needsRecreation also compares path/routingKey, so a row from an older routing model can't survive redeploy as a stale delivery surface * test(slack): pin fail-closed behavior for empty/missing event selection * fix(slack): 409 on custom-bot name collision instead of silently returning the existing credential The service-account dedupe matches on displayName, which defaults to the Slack team name — shared by every bot in that workspace. A second unnamed bot create returned the first credential as success, orphaning the new id already pasted into the Slack Request URL. Same-id replays stay idempotent; different-id collisions now fail loudly so the wizard prompts for a distinct name. * fix(slack): reconnect surfaces Atlassian error codes and persists name/description edits - PUT credential route now returns the Atlassian provider code (providerErrorCode -> code) so reconnect failures map to specific token/domain messages, matching create - Google/Atlassian reconnect send + seed displayName/description (parity with Slack); edits are no longer silently discarded, and empty fields don't clobber existing values * fix(slack): require bot name; propagate rotated bot_user_id to webhooks on reconnect - the setup wizard now requires a bot name (canAdvance), so the credential name, manifest app name, and uniqueness key all use the user's choice instead of the shared Slack team-name fallback that collided for a second bot in one workspace - reconnect that changes the bot user id (recreated Slack app) now updates the bot_user_id cached in each bound webhook's providerConfig, so reaction self-drop keeps working instead of letting the bot's own reactions re-enter --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5d3809a0e3 |
feat(tiktok): add tiktok trigger, block (#5504)
* feat(tiktok): add TikTok integration Adds TikTok as a full OAuth-based integration: provider registration (with TikTok's comma-separated scope and client_key requirements), 9 tools covering profile info, video listing/querying, creator info, direct video/photo posting (URL or file upload), inbox drafts, and post status polling, plus the TikTok block, icon, and generated docs. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tiktok): add avatarFile output to Get User Info Adds a file-typed avatarFile output (sourced from the largest available avatar URL) alongside the existing string avatar fields, so the profile picture can be materialized as a UserFile and chained into file-consuming blocks (e.g. attached to an email), per PR review feedback. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tiktok): lower upload memory cap, drop redundant avatar string outputs Cap the file-upload video buffer at 250MB instead of TikTok's 4GB ceiling — relaying that much through this server's memory per request isn't safe under concurrent load, and larger files can still go through the PULL_FROM_URL path, which never buffers on our server. Also drop the now-redundant avatarUrl/avatarUrl100/avatarLargeUrl string outputs from Get User Info in favor of the file-typed avatarFile output alone, since the feature is unreleased and the raw URL is still reachable via avatarFile.url. Cover image URLs on List/Query Videos are confirmed to be signed, expiring TikTok CDN links; left as strings (no file-output conversion path exists for fields nested inside array items) but documented the expiry behavior more clearly. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(ci): bump API validation route-count baseline for TikTok publish-video route The TikTok integration adds one new Zod-backed internal API route (app/api/tools/tiktok/publish-video), which trips the route-count ratchet in check-api-validation-contracts.ts. Bumping totalRoutes and zodRoutes from 917 to 918 (nonZodRoutes stays 0) to acknowledge the new route is properly validated. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(tiktok): drop unused avatar_url_100 from default user fields After removing the avatar string outputs, avatar_url_100 was still requested from TikTok's user info endpoint but never surfaced anywhere. Removed it from the default field list and the field descriptions, and noted that avatar_url/avatar_large_url feed the avatarFile output. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tiktok): stop returning raw 'credential' subBlock id from tools.config.params The block's params function built a local `credential` variable from params.oauthCredential and returned it under the key `credential` in every switch case. That literal token is the raw subBlock id, which is deleted after canonical transformation into `oauthCredential` — the blocks.test.ts canonical-param-validation suite flags any params function that still references it. It was also redundant: oauthCredential is already part of the base resolved inputs, which the executor merges into the tool call before config.params overrides are applied, so the OAuth token resolution (which reads contextParams.oauthCredential) worked regardless. Removed the explicit credential plumbing, matching the convention already used by other OAuth blocks like dropbox.ts. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tiktok): send empty JSON body on Query Creator Info POST query_creator_info had no request.body function, and formatRequestParams() only attaches a body when tool.request.body is defined at all — so despite sending Content-Type: application/json, the request went out with no body whatsoever. Added body: () => ({}), matching the convention already used by other parameterless-POST tools in this codebase (Google Vault, Supabase, Square, Gmail, etc.). Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tiktok): stop dropping valid zero values in optional numeric fields cursor, photoCoverIndex, and videoCoverTimestampMs all used a truthy check (params.x && {...}) to decide whether to include an optional numeric override, which drops a legitimate 0 (first page has no cursor issue aside, photoCoverIndex 0 is TikTok's own default cover photo, and timestamp 0 is a valid first-frame cover). Switched to explicit undefined/empty-string checks, matching the !== undefined convention the underlying tools already use. In today's resolution pipeline these fields always arrive as strings (even chained block references get stringified by the template resolver), and a non-empty string like "0" is truthy, so this wasn't actively broken end-to-end - but it was relying on that subtlety rather than being correct by construction, and was inconsistent with the tools' own undefined checks. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tiktok): accept newline-separated video IDs in Query Videos videoIds is a long-input (multiline textarea), the same widget used for the newline-separated photoImages field on this block, but its parser only split on commas. Entering one ID per line - the natural pattern for a multiline field, and the one already used elsewhere on this block - produced a single concatenated garbage string instead of an array, so TikTok's query would fail or return nothing. Now splits on commas or newlines, and updated the placeholder/description to reflect both formats. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(tiktok): add app-level webhook ingress and triggers * fix(tiktok): only count actually queued webhook executions Co-authored-by: Cursor <cursoragent@cursor.com> * chore(tiktok): bump API validation baseline for staging merge Co-authored-by: Cursor <cursoragent@cursor.com> * cleanup code * fix type issues * misc code cleanup * remove photos and add upload for videos * move shared video output properties to types.ts so docs generation resolves them Co-authored-by: Cursor <cursoragent@cursor.com> * hide TikTok from toolbar and docs until the integration is ready to ship Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ci): ratchet API validation baseline to 924 after staging merge Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai> |
||
|
|
aa5b1d5569 |
fix(suggested-actions): swap unaudited filled icons for EMCN outline set (#5548)
* fix(suggested-actions): swap unaudited filled icons for EMCN outline set Suggested-action template icons on the home page mixed filled/solid icons in with the app's outline icon convention. Swapped them for consistent outline icons and removed the unused filled Card icon. - gmail.ts: Card -> ClipboardList - clickhouse.ts, sftp.ts: Trash -> TrashOutline - ssh.ts: TerminalWindow (emcn) -> SshTerminalIcon (moved to components/icons.tsx alongside the other block/brand icons) - deleted unused packages/emcn/src/icons/card.tsx - regenerated docs * fix(docs): revert jira.mdx regen regression generate-docs.ts is dropping the Configuration and generic-webhook Output tables for Jira triggers even though the trigger schemas still define those fields (caught by Greptile review). Unrelated to the icon changes in this PR, so reverting jira.mdx to its prior content rather than debugging the generator here. * fix(build): remove last Card icon consumer in playground gallery apps/sim/app/playground/page.tsx imported Card from the top-level @sim/emcn barrel for the icon showcase grid, which I missed when auditing @sim/emcn/icons consumers. Broke the production build after card.tsx was deleted. Removed the import and its gallery entry. Verified with a local `bun run build`. |
||
|
|
6e87d74a93 |
feat(jupyter): add Jupyter integration (contents, kernels, sessions) (#5527)
* feat(jupyter): add Jupyter integration (contents, kernels, sessions) - 16 tools covering Contents, Kernels, Kernelspecs, and Sessions REST APIs - File upload/download via UserFile, following the Box upload pattern - Block with operation dropdown, token auth, and 8 catalog templates - Registered tools + block, generated docs, bumped API validation baseline * fix(jupyter): address Greptile review — SSRF guard, upload path ambiguity, silent notebook fallback - Route uploads through validateUrlWithDNS + secureFetchWithPinnedIP (matches Grafana/1Password pattern) instead of a raw fetch to the user-supplied server URL - Replace the upload path/trailing-slash heuristic with an unambiguous directory + filename split - create_file no longer silently writes an empty notebook when notebook content is malformed JSON — it now errors clearly * fix(jupyter): request content=1 when listing directory contents Without it, Jupyter Server returns directory metadata with content: null, so jupyter_list_contents always reported an empty items array. * fix(jupyter): reject path-traversal segments in Jupyter content paths encodeJupyterPath now rejects '.'/'..' segments across the whole path (shared by all 16 tools, not just upload); the upload route returns a clean 400 when it's hit. * fix(jupyter): close remaining path-traversal and redirect-credential gaps - extract the traversal check out of encodeJupyterPath into a shared assertion, and apply it to body-only path fields (rename newPath, copy copyFromPath, session path) that never flowed through URL encoding and so skipped the check - pass stripAuthOnRedirect to the upload route's secureFetchWithPinnedIP call so a malicious Jupyter server can't redirect the PUT to another origin and receive the caller's token * fix(jupyter): also reject percent-encoded traversal segments A segment like %2e%2e wouldn't match the literal '..' check. Now decodes each segment before comparing, in addition to the literal check, so an already-encoded traversal attempt is caught too. * fix(jupyter): route all 15 remaining tools through an internal proxy for HTTP/private-host support and no redirects The generic external tool executor blocks plain-HTTP and non-localhost private-IP hosts by default, so every non-upload Jupyter operation could fail against typical self-hosted setups (LAN IP, docker hostname, or even literal localhost on a hosted deployment) even though the upload route worked via its own internal route. Added /api/tools/jupyter/proxy (DNS-pinned, allowHttp, maxRedirects: 0) that mirrors the upstream Jupyter response verbatim, matching the established pattern for self-hosted-arbitrary-host integrations (Grafana, 1Password) instead of the generic executor path. Each tool's request block now posts to the proxy instead of building a direct external URL; transformResponse and outputs are unchanged since the proxy response mirrors upstream status/body exactly. Also switches the upload route from stripAuthOnRedirect to maxRedirects: 0 — stronger, since it stops the uploaded file body (not just the token) from ever reaching a redirect target. * fix(jupyter): validate proxy path at the trust boundary, reject path separators in upload filename - The proxy route now independently validates the incoming path field for traversal segments instead of only relying on tool-side validation before the request reaches it — the route is a shared internal boundary, not something only our own tool code can call - The upload route's fileName can come from an advanced override or the legacy fileContent path and could itself contain '/' or '\', silently nesting the upload deeper than the directory param specified. Now rejected outright before joining. * fix(jupyter): decode the whole path before splitting, not per-already-split segment A segment like foo%2f..%2fsecret has no literal slash, so splitting on literal '/' first and decoding each piece in isolation treats it as one opaque segment and never notices the '..' hiding behind the encoded slash. Decode the full path once, then split and check every segment the target server's own single URL-decode pass would see. |
||
|
|
d078c84ee6 |
chore(typescript): upgrade to TypeScript 7 (native Go compiler) (#5521)
* chore(typescript): upgrade to TypeScript 7 (native Go compiler) Bumps typescript to ^7.0.2 across every workspace package. Full bun run type-check/lint/build/test all pass; apps/sim's type-check (the one needing an 8GB heap bump) drops from ~55s to ~7s wall time. Migration fixes required by TS7's stricter defaults: - baseUrl removed: drop it from 5 tsconfigs (paths already resolved relative to tsconfig dir, so behavior is unchanged) and prefix the one bare (non-relative) paths entry each in apps/sim and apps/realtime with './' - moduleResolution=node10 removed: switch packages/cli and packages/ts-sdk to "bundler", matching the rest of the monorepo - types now defaults to [] instead of auto-including every @types/* package: add "types": ["node"] to the shared base tsconfig (this is fundamentally a Node monorepo, so this restores prior behavior in one place instead of duplicating it per-package), add explicit @types/node deps to packages that now rely on it transitively via @sim/db/@sim/logger, and add "declare module '*.css'" to the two packages with plain (non-module) CSS side-effect imports that TS7's stricter checker now flags - packages/logger's isomorphic `typeof window` check no longer needs DOM lib in every consumer: replaced with `'window' in globalThis` - packages/testing and apps/realtime's fetch/DOM mocks need DOM lib where they're compiled, since they model the browser Fetch API - the `typescript` npm package no longer exports the classic Compiler API from its main entry (moved to unstable/ast subpaths); apps/sim's Function-block route used it at runtime to strip import statements from user code, so that one call site now uses Microsoft's official transition package, @typescript/typescript6 - Next.js 16.2.6's own TypeScript-detection heuristic hardcodes a path TS7 no longer ships, and its auto-install fallback assumes npm/pnpm; added @typescript/native-preview as a devDependency to apps/sim and apps/docs so Next detects a valid native compiler instead of trying (and failing) to auto-install one Not merging yet: TS 7.0.2 published today and is still inside this repo's bunfig.toml minimumReleaseAge (7-day) supply-chain gate, so `bun install` will fail for everyone until 2026-07-15. Opening this now to get it through review; hold the actual merge until then. * fix(typescript): address Greptile review findings on TS7 upgrade - packages/logger: 'window' in globalThis treats a shim that leaves globalThis.window explicitly undefined as browser-only, silently dropping production server logs. Restore the original typeof !== 'undefined' semantics via an inline cast instead, so it stays correct without requiring DOM lib in every consumer. - packages/ts-sdk, packages/cli: both are tsc-built, published as Node ESM (package.json "type": "module" with an "exports" map). "moduleResolution": "bundler" is too permissive for that target - it accepts import patterns (e.g. extensionless relative imports) that Node's actual ESM resolver rejects at runtime. Switch both to "module"/"moduleResolution": "nodenext", the correct pairing for a published Node ESM package. Verified real tsc builds (not just --noEmit) still succeed for both. * chore(bunfig): temporarily disable minimumReleaseAge gate for TS7 install TS 7.0.2 published today, still inside the 7-day gate. Lowering to 0 to unblock this merge; will restore to 604800 in an immediate follow-up commit right after merging. |
||
|
|
9404bda5c2 |
docs(forking): workspace forking for enterprises (#5520)
* docs(forking): workspace forking * update comparison links |
||
|
|
d30eb36d9a |
docs(enterprise): add Custom Blocks page (#5515)
* docs(enterprise): add Custom Blocks page Document publishing a deployed workflow as a reusable org-wide block: publishing flow, common uses, using a block, managing, and self-hosted setup, with UI screenshots. Register in the enterprise sidebar and wire the settings docsLink. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvLuS5o1eFjBBhEC4PytcA * docs(enterprise): drop explicit Enterprise framing, note access control The page's Enterprise placement is implicit, so remove the "Enterprise feature" callout and plan mentions. Add that custom blocks can be allowlisted per permission group via Access Control. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvLuS5o1eFjBBhEC4PytcA * docs(enterprise): remove Self-hosted setup section from Custom Blocks Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvLuS5o1eFjBBhEC4PytcA --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
767307177e |
fix(comparison): correct stale and unsourced claims on comparison pages (#5513)
* fix(comparison): correct stale and unsourced claims on comparison pages - Remove stale "Templates" references from the LangChain page and related blog posts (feature was removed platform-wide) - Re-verify every sourced claim across all comparison-page profiles (Sim's own facts and all 20 competitor profiles) against live sources; fix dead links, renamed products, outdated figures, and claims no longer supported by their citation - Fix a stale sidebar permissions reference in the enterprise access-control docs * fix(comparison): use consistent URL format for n8n AI Workflow Builder citation |
||
|
|
61ceedc5b9 |
fix(notion): align integration with live API docs, add block retrieval coverage (#5491)
* fix(notion): align integration with live API docs, add block retrieval coverage
- wire up notion_update_page in the block (was registered but unreachable — no dropdown option or subBlocks)
- fix legacy NotionBlock outputs to cover all 17 operations, not just content/metadata
- trim ID params (pageId, databaseId, parentId) before use in request URLs across 8 tool files
- remove content required:true on create_page (Notion allows title-only pages)
- remove dead unused NotionReadDatabaseParams interface
- add notion_retrieve_block/_v2 tool for GET /v1/blocks/{id}, filling a gap in block CRUD coverage
* fix(notion): second validation pass — pagination gaps, phantom outputs, dead types, regen docs
- add missing startCursor/start_cursor pagination param to notion_query_database and notion_search
- fix notion_search missing from the shared pageSize/startCursor field's operation condition in NotionBlock
- fix NotionV2Block title/url/created_time/last_edited_time outputs rendering unconditionally for every operation instead of only the ones that return them
- remove 153 lines of dead unused output-shape constants from types.ts
- regenerate integration docs
* fix(notion): expose retrieve-block outputs in legacy block picker
type/block/archived only conditioned on notion_update_block/notion_delete_block,
and has_children was missing entirely — Retrieve Block's payload was invisible
in the legacy NotionBlock output picker (Greptile P1 on PR #5491).
* fix(notion): document type-specific content gap on retrieve_block output
block.properties (BLOCK_OUTPUT_PROPERTIES) only covers common block fields,
not the type-specific sub-object (paragraph.rich_text, image.file, etc.) —
that varies per block type and isn't enumerable. Documented in the
description per Greptile P2 on PR #5491; no functional change, block: data
already carries the full object at runtime.
|
||
|
|
ed1492bcbd |
fix(google-vault): validate against live API docs, add matter/hold/saved-query CRUD coverage (#5482)
* fix(google-vault): validate integration against live API docs, add matter/hold/saved-query CRUD coverage
- Fix critical bug: create_matters_export sent the deprecated Query.searchMethod field (deprecated 2019, support ended 2020) instead of method, silently breaking account/org-unit scoped exports
- Fix duplicate matterId subBlock id (two definitions collided across operations)
- Add matter lifecycle: update, close, reopen, delete, undelete
- Add matter collaborator management: add/remove permissions
- Add export delete
- Add hold update, delete, add/remove held accounts
- Add saved query create/list/delete
- Bump tool versions to 1.0.0 to match repo convention
- Fix docsLink to point at docs.sim.ai instead of the vendor site
- All new endpoints are covered by the existing ediscovery + devstorage.read_only OAuth scopes (no new scopes requested)
* fix(google-vault): pageToken should be user-or-llm visibility, not hidden
Greptile review: hidden is reserved for framework-injected tokens; pageToken
in list_saved_queries.ts should be user-or-llm so an agent/user can pass it,
matching the pattern used elsewhere for tool-supplied pagination cursors.
* fix(google-vault): fail loudly instead of silently clearing hold scope on update
Cursor Bugbot: PUT holds/{id} replaces the full resource — a name/query-only
update with no accountEmails/orgUnitId would silently drop the hold's
custodian coverage. Now throws a clear error directing callers to resend the
scope or use add_held_accounts/remove_held_accounts for incremental changes.
* fix(google-vault): reject unscoped exports/saved-queries for non-MAIL corpus
Independent audit (parallel doc-verification pass): create_matters_export
and create_saved_query resolved Query.method to undefined when corpus
wasn't MAIL and no accountEmails/orgUnitId was given, silently sending an
invalid request (method is a required Query field) instead of a clear
error. Now throws with an actionable message before the request is sent.
* fix(google-vault): document full-replace semantics on hold update query filters
Cursor Bugbot: update_matters_holds only sets query.mailQuery/groupsQuery/
driveQuery when terms/date/shared-drive fields are provided, but the PUT
replaces the whole hold — omitting a previously-set filter clears it, not
leaves it unchanged. Filters are legitimately optional (a hold may have
none), so this can't be hard-required like scope; instead the tool and
field descriptions now explicitly state the full-replace behavior and
direct callers to resupply current values via Vault List Holds first.
* fix(google-vault): isolate stale-value-prone subblocks per operation
Cursor Bugbot: consolidating shared subblocks across operations left two
cross-contamination risks since a stale value from one operation stays in
block state until overwritten:
- accountEmails/orgUnitId are checked emails-first with silent either/or
priority; sharing them across update_matters_holds and create_saved_query
meant a leftover value from a different operation could silently override
the intended scope. Gave both operations dedicated fields
(updateHoldAccountEmails/updateHoldOrgUnitId, savedQueryAccountEmails/
savedQueryOrgUnitId), remapped in tools.config.params.
- matterId presence alone switches google_vault_list_matters between
list-all and single-get. Sharing it with every other operation meant a
leftover matterId could silently turn "List Matters" into a single-matter
fetch. Gave list_matters its own optional listMatterId field.
create_matters_holds/create_matters_export keep sharing accountEmails/
orgUnitId as before this PR (pre-existing behavior, not introduced here).
* fix(google-vault): isolate list-optional-id fields from required-elsewhere counterparts
Cursor Bugbot: exportId/holdId/savedQueryId were shared between their
respective list operation (optional filter) and update/delete/held-account
operations (required). A stale ID left over from a delete/update on the
same block instance would silently narrow the corresponding list operation
to a single-resource get instead of listing the collection. Gave each list
operation its own dedicated optional field (listExportId, listHoldId,
listSavedQueryId), remapped in tools.config.params — same pattern already
used for listMatterId.
* fix(google-vault): defensively order mutually-exclusive scope spreads
Greptile: savedQueryAccountEmails/savedQueryOrgUnitId spread after their
updateHold* counterparts, so if both were ever truthy at once the wrong
one would silently win. In practice they're mutually exclusive (each only
populated while its own single 'operation' value is selected, so at most
one pair is ever truthy), but reordering costs nothing and removes any
doubt about precedence.
* docs: regenerate integration docs
Reruns scripts/generate-docs.ts against the current block/tool/trigger
registry. Picks up google_vault's new operations plus everything else that
had landed on staging without a docs regen (bigquery, google_calendar,
google_maps, onedrive, microsoft_teams, gitlab, github, discord, dropbox,
and others), plus icon and integrations.json updates.
|
||
|
|
1a87f1a83c |
fix(microsoft-planner): align with live Graph API docs, add plan CRUD + categories (#5486)
* fix(microsoft-planner): align with live Graph API docs, add plan CRUD + categories
- fix update_task never returning updated task (missing Prefer: return=representation)
- fix wrong @odata.type on task assignments (missing leading #)
- fix update_plan/update_plan_details/update_bucket/update_task_details crashing
on 204 No Content responses to PATCH (Graph sometimes ignores Prefer header)
- add .trim() on path IDs across tools invoked outside the block
- add create_plan, update_plan, delete_plan, get_plan_details, update_plan_details
tools (If-Match/etag handled correctly on update/delete)
- add appliedCategories support on create_task/update_task
- all new tools verified against Graph API Permissions tables to require only
scopes we already request (Group.ReadWrite.All, Group.Read.All, Tasks.ReadWrite)
- regenerate integration docs
* fix(microsoft-planner): use Graph-specific error extractor consistently
7 tools were pinned to the generic 'nested-error-object' extractor
instead of MICROSOFT_GRAPH_ERRORS, losing Graph's inner-error detail
(e.g. ETag mismatch specifics) that sibling Microsoft integrations
(Excel, OneDrive, SharePoint) already surface correctly.
* fix(microsoft-planner): address Cursor/Greptile round-1 review findings
- fix empty priority/percentComplete string coercing to 0 (Number('')) at
the block layer, which Planner treats as urgent/0% instead of leaving unset
- support removing applied categories on update via a "-category3" prefix,
since Graph only clears a label when its key is explicitly set to false
- add missing MICROSOFT_GRAPH_ERRORS extractor to delete_plan/get_plan_details
* fix(microsoft-planner): don't return a stale etag on 204 update_task response
Graph's If-Match update changes the resource's etag even when it returns
204 No Content instead of the updated representation. Returning the
request's (now-stale) etag as if it were current would let a chained
update silently send a wrong If-Match and fail with 412. Return an empty
etag instead so update_task's own etag-required guard forces a re-fetch.
|
||
|
|
24ebba9acc | chore(credential-sets): cleanup feature (#5460) | ||
|
|
719179258c |
improvement(rich-markdown-editor): table column-resize cursor, exhaustive paste tests, editor docs (#5455)
* fix(rich-markdown-editor): show the col-resize cursor on table column borders prosemirror-tables toggles a `resize-cursor` class on the editor while the pointer is over a column boundary, but there was no rule to change the cursor — the blue resize handle showed with no cursor affordance. Add the scoped `col-resize` rule. * test(rich-markdown-editor): exhaustive markdown paste coverage Cover every rich construct (headings, marks, lists, task lists, blockquote, code block, image, thematic break, table), markdown parsed despite an HTML sibling, multi-block order, read-only rejection, and the defer/verbatim cases for non-markdown input. * docs(editor): add rich markdown editor page Document the inline rich markdown editor — formatting, structure, lists, tables, code blocks, images, the slash menu, and markdown fidelity — with a rendered overview screenshot. * test(rich-markdown-editor): scope paste tests to what MarkdownPaste actually gates Inline-only marks (single-asterisk italic, ~~, single-backtick code) are intentionally not detected by looksLikeMarkdown (single `*` would false-positive on e.g. `*args`); they route through the Markdown extension's own paste path, not MarkdownPaste. Move them from the rich-render cases to the defers-to-default cases so the suite tests the handler it names. |
||
|
|
fb3f95d5dc |
feat(aws): expand SES/STS/Secrets Manager tool coverage, fix API alignment gaps (#5450)
* feat(aws): expand SES/STS/Secrets Manager tool coverage, fix API alignment gaps
- SES: add suppression list management, email identity CRUD, template
update, configuration set creation, custom verification email (10
new tools); fix silent httpsPolicy drop in create_configuration_set
and unvalidated suppression reason enum in list_suppressed_destinations
- STS: add AssumeRoleWithWebIdentity and AssumeRoleWithSAML (unsigned,
no static credentials required); extend assume_role with
policyArns/tags/transitiveTagKeys session params
- Secrets Manager: add describe_secret, tag_resource, untag_resource,
restore_secret, rotate_secret; fix list_secrets dropping
rotation/version metadata fields; normalize tool versions to 1.0.0
and alphabetize registry entries
All 31 tools verified param-by-param against live AWS API docs across
two independent audit passes.
* fix(aws): address review findings on SES config/identity and Secrets Manager rotation
- ses_create_configuration_set: validate suppressedReasons against
BOUNCE/COMPLAINT enum before calling AWS (was silently reaching AWS
as a generic 500 for bad values); tags now a proper Zod array schema
instead of a string with route-side JSON.parse
- ses_create_email_identity: dkimSigningAttributes and tags now proper
Zod object/array schemas instead of strings with route-side
JSON.parse, matching the pattern used elsewhere (e.g. sts_assume_role
tags, secrets_manager_tag_resource)
- secrets_manager_rotate_secret: reject automaticallyAfterDays and
scheduleExpression when both are supplied — AWS RotationRules
accepts only one
- sts createUnauthenticatedSTSClient: corrected a misleading comment
claiming these calls are fully unsigned; the SDK still falls through
its default credential provider chain
* fix(ses): correct json input types for tags/dkimSigningAttributes
The SES block declared the tags and dkimSigningAttributes block inputs
as 'string' instead of 'json', so the generic block executor never
parsed the JSON code-editor value before forwarding it — workflow runs
sent a raw JSON string where the contract now expects a structured
object/array, failing validation. Also corrected the corresponding
tool param TypeScript types, which were still typed as string | null.
* fix(ses): stop coercing switch string 'false' to true in create_configuration_set
Boolean('false') evaluates to true, so turning off the
reputationMetricsEnabled or sendingEnabled switch sent the opposite of
the user's choice to SES. Match the established === 'true' string
comparison pattern used elsewhere in the codebase.
* fix(sts): stop double-parsing assume_role session tags input
tags was declared as a 'json' block input, so the generic executor
JSON.parse'd it before the switch-case handler ran — but that handler
already converts the raw table-rows array (or a passthrough string)
into the JSON string the sts_assume_role contract expects. Declaring
it 'json' broke that conversion for non-string inputs. Reverted to
'string' so the handler's existing string/array disambiguation runs
on the untouched raw value.
* fix(sts): supply placeholder credentials to the unauthenticated client
createUnauthenticatedSTSClient omitted credentials entirely, so the
SDK's signing middleware fell through the default credential provider
chain and threw CredentialsProviderError before the request was sent
in any environment with no ambient AWS identity — even though
AssumeRoleWithWebIdentity/AssumeRoleWithSAML never check the
signature. Static placeholder credentials skip that resolution
without granting or requiring any real IAM identity.
|
||
|
|
918ba8a86a |
feat(ahrefs): validate integration, fix cents/column bugs, add 13 v3 endpoints (#5447)
* feat(ahrefs): validate integration, fix cents/column bugs, add 13 v3 endpoints Audited the existing Ahrefs integration against live API v3 docs and fixed real bugs: broken_backlinks selected the wrong column (http_code_target instead of http_code), and keyword_overview/metrics/metrics_history returned CPC/cost fields in USD cents without converting to USD. Also fixed the top-pages mode dropdown missing the "exact" option. Added 13 new tools covering previously-unsupported Ahrefs v3 endpoints: Rank Tracker (overview, SERP overview, competitors overview, competitors stats), Batch Analysis, Site Audit page explorer, four history/trend endpoints (domain rating, metrics, referring domains, keywords), Related Terms, Anchors, and Paid Pages. Note: the CPC/cost unit fix silently shifts existing organicCost/paidCost/cpc values by 100x for any workflow already consuming them (the old values were wrong, in cents instead of dollars). * fix(ahrefs): convert remaining cents-to-USD fields, drop unneeded fallback key Rank Tracker SERP Overview's value field, Competitors Stats' trafficValue, and Competitors Overview's nested competitor value were all left in USD cents while every other monetary field in the integration converts to USD - fixed for consistency with the rest of the integration. Also drops the unverified `data.results` fallback in Batch Analysis; the Ahrefs v3 docs confirm the response key is always `targets`. * docs(ahrefs): regenerate docs to reflect cents-to-USD conversion fix The generated docs page was stale after the previous commit converted rank_tracker_serp_overview.value, rank_tracker_competitors_stats.trafficValue, and rank_tracker_competitors_overview's nested value from cents to USD - regenerating picks up the corrected field descriptions. * fix(ahrefs): revert incorrect broken_backlinks column, fix missed top_pages conversion, revert unverified competitor value conversions Independent re-verification against live Ahrefs v3 docs surfaced two real regressions from the earlier fix rounds and one missed conversion: - broken_backlinks: the earlier fix changed the selected column from http_code_target to http_code, but the docs say http_code is the *referring page's* status and http_code_target is the *broken target page's* status - the tool needs the latter (matches its own output description). Reverted to http_code_target. - top_pages: value field was never divided by 100 despite the docs stating it's in USD cents and the output already claiming USD - fixed. - rank_tracker_competitors_stats.trafficValue and the nested competitor.value in rank_tracker_competitors_overview were converted from cents to USD last round on a "match every other monetary field" assumption, but the live docs do not document these two fields as cents (unlike every field that was correctly converted). Reverted to passthrough and dropped the "(USD)" claim from their descriptions until Ahrefs documents the unit. Also added the missing "exact" mode option to top_pages' mode param description, matching every sibling tool. * fix(ahrefs): convert rank tracker competitor value/trafficValue to USD Both bots independently flagged these two fields as inconsistent with every other monetary field in the integration, all 7 of which are explicitly documented as USD cents. Neither field has explicit unit documentation (one is undocumented as cents, the other's schema isn't statically retrievable at all), but given the unanimous pattern across every other verified field and no contrary evidence, converting for consistency is the better bet than leaving them as an outlier. * style(ahrefs): drop non-TSDoc inline comments introduced in this PR Repo convention disallows non-TSDoc comments; removed the three explanatory // comments this PR added next to cents-to-USD conversions (keyword_overview, paid_pages, related_terms) - pre-existing comments elsewhere in the file are untouched, out of scope for this PR. * fix(ahrefs): default optional country to us consistently across all tools paid_pages, metrics_history, keywords_history, and batch_analysis were the only 4 of the 11 tools with an optional country param that didn't fall back to "us" when omitted, unlike domain_rating, metrics, keyword_overview, organic_keywords, organic_competitors, top_pages, and related_terms - all of which default client-side. Aligned all four to the same convention so direct tool/agent calls without an explicit country get the same behavior regardless of which operation is used. * fix(ahrefs): split shared date subBlock id for datetime-format operations site_audit_page_explorer and rank_tracker_serp_overview both reused the generic 'date' subBlock id with YYYY-MM-DDThh:mm:ss semantics, while every other operation using that same id expects YYYY-MM-DD. Switching operations without clearing the field could carry a stale wrong-format value into the new operation's request. Split into distinct ids (crawlDate, asOfDate) mapped back to each tool's date param in tools.config.params. |
||
|
|
6d5ac583bc |
fix(cloudflare): align integration with live API, fix type-coercion bug (#5444)
* fix(cloudflare): align integration with live API, fix type-coercion bug
- fix update_zone_setting body builder: was blindly JSON.parse-ing every
value, silently coercing scalar settings (e.g. min_tls_version "1.2")
to the wrong type; now only parses object/array literals
- fix list_dns_records name/content/tag filters to use Cloudflare's
exact-match dotted param names (name.exact/content.exact/tag.exact)
- remove auto_minify (never a real setting ID) and minify (deprecated
and removed from the live zone-settings API in Aug 2024)
- remove dead jump_start param from create_zone (not part of the
current POST /zones schema)
- fix block bgColor from #F5F6FA to Cloudflare's actual brand orange
#F38020
- add missing secondary zone type option and plan.id sort option
- expand BlockMeta skills/templates
* fix(cloudflare): reject empty browser_cache_ttl value instead of coercing to 0
* fix(cloudflare): address Greptile review feedback
- use trimmed value consistently in update_zone_setting fallback paths
- document that tag_match has no effect with a single exact-match tag
filter (Cloudflare's API only combines multiple tag conditions)
* fix(cloudflare): coerce non-string setting values before trim
Wand-generated or block-referenced values can arrive as a number at
runtime despite the declared string param type, which crashed the
body builder's .trim() call.
* fix(cloudflare): harden null/undefined value handling, fix stale tag description
- coerce null/undefined value to empty string instead of literal
"null"/"undefined" strings before trimming
- fix stale block-level 'tag' input description left over from the
comma-separated-tags -> exact-match-tag filter fix
* fix(cloudflare): pass through structured array/object values as-is
A block-referenced array/object value was being blindly stringified
before the JSON-shape check, so String([...]) comma-joined arrays and
String({...}) produced the literal "[object Object]" instead of the
JSON shape Cloudflare expects (e.g. for ciphers). Already-structured
values now pass straight through.
* revert(cloudflare): keep original block bgColor (#F5F6FA)
|
||
|
|
3e710845da |
improvement(academy): set 2, pages for the new video wave (workflow embeds, 2K players, full sequencing) (#5382)
* academy: Chat section (intro, building) + Agents tool-calling and skills pages, sequenced in meta.json — pages for the four approved videos, blob src pattern, chapters offset by each recorded intro
* academy pages: Related documentation links point at real docs routes (mothership/agents/logs-debugging/deployment), not other academy videos
* academy: Tables — Workflow Columns page (combined-cut chapters, receipts pedagogy), sequenced after tables/intro
* academy set 2: five new pages (agents/block, agents/memory, knowledge-bases/connectors, tables/operations, files/object) + retimed chapters on the redone tables/files intros (recomposed/working-day cuts) + full meta.json sequencing — Chat · Agents(5) · Tables(3) · Files(2) · KB(2)
* academy pages: every page shows the VIDEO's workflow (new academy-video-workflows.ts registry — invoice intake, Qualify, memory, table ops, tools/skills agents, support-desk, content-agent) + plain pedagogical headings throughout (no poetry: 'The warm and cold split' → 'The same agent, with and without memory', 'From one pass to a loop' → 'What tools change', etc.); files/intro's embed swapped to the video's machine
* academy: all video players point at the 2K blob set (academy/<slug>.mp4) — 20 pages rewritten from academy-preview/*-light-with-intro, files/intro plays the new files-intro cut, workflows/logs gains its src (video now exists); use-case placeholders stay src-less (no videos yet)
* biome: format academy-video-workflows.ts
* fix (cursor/greptile): support-desk condition block uses branches + rows:[] (the renderer's real fields — conditions: was never read, so Urgent? rendered without if/else handles and branch edges dangled); bgColor aligned to the product condition example
* biome: format meta.json (CI lint:check)
* fix (cursor): Start exposes <start.input>, not <start.ticket>/<start.idea> — support-desk Triage and content-agent Writer message rows now match the product's Start output (same pattern as the file's other workflows)
* academy lesson material (CTO ask): FAQ on every lesson page + the index (grounded in the audited codebase facts: memory modes, the ten table ops, coerce/refuse, deploy surfaces + draft-vs-deployed, file parser formats; the FAQ component emits FAQPage JSON-LD for SEO); em-dashes 202 → 0 with hand-fixed splices; added copy where sparse (agent block placement, when-to-use-memory); SEO phrasing sprinkled naturally into FAQ answers ('visual platform for building AI workflows and agents', 'no-code', model names), not over-indexed
* fix: quote frontmatter descriptions that gained colons in the em-dash pass (unquoted YAML scalars with a second colon broke every page)
|
||
|
|
10b6bb33e9 |
feat(google-appsheet): add Google AppSheet integration (#5376)
* feat(google-appsheet): add Google AppSheet integration
- 4 tools (find/add/edit/delete rows) against the AppSheet Action API
- API key auth via Application Access Key (no OAuth/scopes needed)
- Block with operation dropdown, region selector, and Selector expression support
- Generated docs
* improvement(google-appsheet): harden response parsing, add wand config and skills
- Guard against empty/non-JSON AppSheet response bodies (Delete may return no body)
- Add wandConfig to the Selector field for AI-assisted expression generation
- Add 3 skills grounded in attested AppSheet/Zapier automation patterns
- Tighten json output descriptions to describe inner shape
* fix(google-appsheet): validate region against allow-list, encode appId, validate rows shape
- Reject unrecognized region values instead of interpolating them into the
request host (a caller could otherwise redirect the Application Access
Key to an arbitrary domain)
- URL-encode appId, not just tableName, in the Action endpoint path
- Reject non-array Rows input in tools.config.params instead of forwarding
a single object to the AppSheet Action API
- Drop the mismatched json-object generationType on the rows wand config
(that enricher appends "must start with { and end with }", which
conflicts with the JSON-array shape the field expects)
- Add utils.test.ts covering region validation and response-body parsing
* docs(google-appsheet): add manual intro/getting-started section
Match the MANUAL-CONTENT convention used by other integration docs
(Airtable, Ahrefs, Google PageSpeed) — an overview of the service, what
the Sim integration lets agents do, and how to get an Application
Access Key.
* docs: sync generated integration docs with current source
Regenerate docs for integrations whose tools/blocks changed upstream
without a matching docs regen (ahrefs, algolia, amplitude, brex, clerk,
gong, hex, langsmith, loops, onepassword, sendgrid, sharepoint,
similarweb, supabase, tailscale, trello, vercel, wordpress), plus the
integrations.json catalog.
|
||
|
|
997740790d |
feat(fathom): add list meeting types tool and missing list-meetings filters (#5359)
* feat(fathom): add list meeting types tool and missing list-meetings filters - Add fathom_list_meeting_types tool (GET /meeting_types) - Add missing list-meetings params: includeHighlights, meetingType, calendarInviteesDomains, calendarInviteesDomainsType - Add missing meeting response fields: meeting_type, meeting_url, shared_with, highlights - Wire new operation and filters into the Fathom block * fix(fathom): expose meeting_url/highlights in outputs, stop force-sending domain type default - Add meeting_url and highlights to list_meetings outputs schema so they're addressable from downstream blocks (Greptile P1) - Drop the forced 'all' default on calendarInviteesDomainsType so the filter is only sent when a user explicitly picks a value (Greptile P2) * fix(fathom): never send calendar_invitees_domains_type=all to the API Match the existing Fathom connector's guard (meetingType !== 'all') so the request omits the param entirely when the value is the API's own default, regardless of what the dropdown shows selected in the UI. * fix(fathom): fully expose list_meetings meeting fields in outputs schema transformResponse already returned meeting_title, scheduled/recording times, recorded_by, calendar_invitees, default_summary, transcript, action_items, and crm_matches, but outputs.meetings.items.properties only documented a curated subset, leaving these fields unaddressable from downstream workflow blocks. Complete the schema to match the full Meeting object Fathom's API returns. * fix(fathom): mark recording_id optional in list_meetings outputs transformResponse maps recording_id as meeting.recording_id ?? null and the response type already types it number | null; the outputs schema now reflects that nullability. * fix(fathom): mark calendar_invitees email optional in list_meetings outputs Fathom's docs mark Invitee.email as nullable; the outputs schema now reflects that instead of declaring it as always-present. |
||
|
|
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
|
||
|
|
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). |
||
|
|
c1b84e4eec |
feat(linq): audit fixes + native auto-registering webhook trigger (#5301)
* feat(linq): audit fixes + native auto-registering webhook trigger Tools/block audit (validated against the live Linq partner API + OpenAPI spec): - create_chat: read the sent message from top-level response.message (was always null) - get_message/edit_message: expose canonical deliveryStatus; mark is_delivered/is_read deprecated - send_message: fall back to from_handle.service/preferred_service for service output - list_phone_numbers: migrate deprecated health_status to reputation; add forwardingNumber; guard JSON parse - check_imessage/check_rcs: guard response.json() parse - mark_chat_read: note 1:1-only / group no-op behavior Native webhook trigger (auto register + deregister): - 6 triggers (message received/delivered/failed/read, reaction added, all-events) - Standard Webhooks signature verification (HMAC-SHA256, whsec_ secret) - createSubscription/deleteSubscription manage the Linq subscription lifecycle - event_id idempotency; full 27-value WebhookEventType enum for all-events - regenerated docs * refactor(linq): drop phantom create_chat response path, complete deliveryStatus enum doc Final validation against the raw Linq OpenAPI spec confirmed the sent message is at chat.message (CreateChatResult exposes only chat), so the data.message fallback was dead code. Also list all 7 DeliveryStatus values in the send_message output description. * fix(linq): namespace trigger credential keys, handle edit_message 204, nullable forwardingNumber Review + final pre-merge audit fixes: - Trigger apiKey/phoneNumbers subblocks collided with the block's tool apiKey state key — rename to triggerApiKey/triggerPhoneNumbers (per the namespacing rule from #2133) and read them in the webhook handler - edit_message: the API returns 204 No Content when editing an already-deleted message; guard the empty body instead of throwing on response.json() - list_phone_numbers: mark forwardingNumber output nullable (returns null) - check_rcs: tighten address hint (RCS is phone-only, not email) - regenerated docs * fix(linq): read triggerApiKey in webhook deleteSubscription deleteSubscription still read config.apiKey after the credential rename, so undeploy would skip the DELETE and orphan the Linq subscription. Match createSubscription's triggerApiKey key. * fix(linq): mark list_phone_numbers healthStatus output nullable healthStatus returns null when Linq omits reputation/health_status; declare nullable: true to match the runtime value (same as forwardingNumber). * fix(linq): mark nullable list_webhook_subscriptions item fields phoneNumbers/createdAt/updatedAt are null-coerced by mapWebhookSubscription; declare nullable: true on the array-item schema to match runtime (consistent with the top-level webhook outputs' optional flags). |
||
|
|
604d03e40d |
improvement(sendblue): audit fixes for optional group numbers, seat_id, typing state/duration (#5300)
* improvement(sendblue): audit fixes — optional group numbers, seat_id, typing state/duration * fix(sendblue): guard group recipients and typing state/duration before request * fix(sendblue): omit empty numbers array from group message body * test(sendblue): add webhook handler tests; trim group_id and normalize empty group_id to null * fix(sendblue): trim and drop blank group recipients before target guard |
||
|
|
d0aed14a0f |
feat(integrations): wave-4 tool-depth (Slack/Asana/Jira/Google Docs/Trello/Monday) + context.dev validation (#5289)
* fix(context_dev): validation pass — add search numResults/country, accuracy fixes Comprehensive /validate-integration of all 22 context.dev tools against the live API docs found the integration clean (no correctness bugs). Applied the actionable items: - search: expose numResults (10-100) + country inputs (API supported them; users were silently capped at 10 results) - accuracy: scrape_html type description (+doc/docx), map meta description (+sitemapsSkipped), brand links description (+contact) - robustness: trim string query values in appendParam * feat(integrations): wave-4 tool-depth — Slack, Asana, Jira, Google Docs, Trello, Monday Deepen six existing blocks with 38 new tools, no new OAuth scopes (all under already-granted scopes), additive/backwards-compatible: - Slack (7): schedule/list/delete scheduled messages; archive/rename/set-topic/set-purpose conversation - Asana (8): create/get project, list workspaces, create subtask, delete task, add followers, create/list sections (via internal routes + contracts) - Jira (5): list/get project, get transitions, list issue types, get fields - Google Docs (6): delete content range, named ranges, paragraph bullets, update paragraph style (documents.batchUpdate) - Trello (7): create board/list, get board/card, add checklist/label/member - Monday (5): change column value, create board/column, get groups, duplicate item Route baseline 873->881 for the 8 new Asana internal routes. * fix(integrations): wave-4 validation pass — fix alignment enum, GraphQL input-object, scope/UI gaps Comprehensive /validate-integration of all 6 modified integrations (existing + new tools) vs live API docs. Fixes: - google_docs: CRITICAL alignment enum LEFT/RIGHT/JUSTIFY -> API enum START/END/JUSTIFIED (mapped); namedStyleType 'unchanged' option; 'zero-based' index wording - monday: CRITICAL search_items columns now emits GraphQL input-object with unquoted keys (was always failing the non-cursor branch) - slack: schedule_message DMs via user-id-as-channel; add channels:manage/groups:write/reactions:read scope descriptions; nextCursor optional - jira: list_projects expand=lead so lead outputs populate (was always null) - trello: get_actions limit now applies to the card path too - asana: add missing 'completed' + 'projects' subBlocks (were unsettable in UI); request permalink_url via opt_fields on create routes * fix(integrations): clamp context.dev search bounds; precise Google Docs index wording - context_dev/search: clamp numResults to the documented 10-100 range; normalize country to trimmed uppercase - google_docs: replace ambiguous '1-based'/'zero-based' index wording with the concrete fact (the document body starts at index 1), matching buildInsertLocation (index<1 appends) and buildContentRange * fix(integrations): validate context.dev country (ISO-2); regenerate google_docs docs - context_dev/search: reject non-2-letter country values with a clear error instead of forwarding them - docs: regenerate google_docs.mdx so the public index-contract wording matches the updated tool descriptions (body starts at index 1) * fix(asana): omit completed unless explicitly set (don't send false on unchecked) The new completion checkbox mapped an unchecked/untouched state to completed:false, which made update_task silently un-complete tasks and search_tasks filter to incomplete. Now only sends completed when the box is checked (undefined otherwise). * fix(slack): expose Destination toggle for schedule_message so DM scheduling is reachable The mapper already routes schedule_message DMs (user-id-as-channel); add schedule_message to the destinationType condition so users can deliberately choose Channel vs DM instead of it only triggering via leftover state. |
||
|
|
7545391cb3 |
feat(docs): render workflow previews with the shared editor renderer (#5277)
* chore(workflow-renderer): declare @sim/emcn dep + wire the package into docs
Adds the missing @sim/emcn peer/dev dependency to @sim/workflow-renderer (it imports @sim/emcn in every View but resolved only via workspace hoisting). Wires apps/docs to consume @sim/workflow-renderer (dependency, transpilePackages, Tailwind @source) and adds remark-breaks (pulled transitively via the barrel's NoteBlockView export) — mirroring the @sim/emcn integration. Foundation for migrating the docs workflow-preview fork onto the shared Views. Build resolves the package/@source/remark-breaks cleanly.
* feat(docs): render loop/parallel containers with the shared SubflowNodeView
Replaces the forked PreviewContainerNode with a thin DocsContainerNode that maps the static preview data to SubflowNodeView's read-only (isPreview) props — no stores or hooks. Adds the block size to the preview node data so the view can size itself, and corrects the parallel example's start-edge handle id to 'parallel-start-source' (the view derives the handle id from kind). Deletes preview-container-node.tsx. Container colors/icons are now owned by the shared view (loop=blue, parallel=yellow).
* feat(docs): render block nodes with the shared WorkflowBlockView
Replaces the forked PreviewBlockNode with a thin DocsBlockNode that maps the static preview data to WorkflowBlockView's props — store-free, builds the subblock rows (condition/router Context+routes/default + tools + error) via SubBlockRowView, strips branch-id prefixes so the view's regenerated handle ids match, remaps router->router_v2, and keeps the framer-motion dim/stagger wrapper. Promotes resolveIcon into block-icons.tsx, adds the --workflow-edge token to docs global.css, deletes preview-block-node.tsx. The canvas diagrams now render with the real editor's view.
* refactor(workflow-renderer): make editor-only WorkflowBlockView props optional
The child-deploy, schedule, and webhook badge props (and their callbacks) only matter in the editor. Mark them optional and optional-chain the three callbacks so read-only consumers (docs, academy) can omit the whole group instead of passing ~18 explicit off-values. The editor still passes them, so its behavior is byte-identical (verified: apps/sim type-check clean). DocsBlockNode drops the off-props.
* feat(docs): replace how-it-runs static diagrams with live WorkflowPreview
Swaps the four static PNGs on the how-it-runs page for live, app-styled WorkflowPreview diagrams (concurrency, combination, condition+router branching, error path). Adds the four example workflows and renders error-port edges red to match the editor. The English page only; the translated execution/basics pages keep the PNGs.
* refactor(workflow-renderer): the view owns condition/router/error rows
Both the editor container and the docs adapter hand-built the condition/router/error summary rows in an order that had to stay in lockstep with the view's absolute handle-offset math — a three-way coupling with nothing enforcing it. The view now renders those rows itself from the conditionRows/routerRows it already receives (plus a routerContextValue prop for the router's Context row), so row order and handle geometry live together in one place. Both containers pass only data and their non-branch rows.
Editor is byte-identical: getDisplayValue moves to where conditionRows/routerRows are built; the no-subBlock SubBlockRow path is already an exact SubBlockRowView(title, value) passthrough; the error row stays gated on shouldShowDefaultHandles. Verified apps/sim type-check clean. Docs now also renders the error row on condition/router blocks, which the real editor already did (shouldShowDefaultHandles is true for them) — an alignment fix.
* refactor(docs): drop the parallel --wp-* token layer for the app/emcn tokens
The workflow-preview ran a 25-token --wp-* mirror (22 were pure aliases of app tokens docs already defines) plus a .wp-scope wrapper class. Replaces every var(--wp-X) with its canonical app/emcn token (--wp-edge->--workflow-edge, --wp-highlight->--brand-secondary, badges->--badge-*, etc.), adds the one missing token (--divider), and deletes the .wp-scope blocks + class. Visually identical (aliases resolve to the same values); the preview now inherits the same design tokens as the shared views and the rest of the app instead of a hand-rolled parallel set.
* refactor(docs): adopt emcn Badge + dedup resolveIcon in workflow-preview
output-bundle's hand-rolled type badge (BADGE_COLORS + a styled span) becomes the emcn Badge (its green/blue/orange/purple/gray variants use the identical --badge-* tokens). resolveIcon, which had three copies, is now imported once from block-icons by output-bundle and block-inspector.
* refactor(docs): rebuild the preview inspector on emcn chip primitives
The lightbox inspector was a hand-rolled facsimile (raw divs + a CONTROL class string + inline dashed borders). It now composes from the same @sim/emcn primitives the live editor's sub-block controls wrap — ChipSelect/ChipInput/ChipTextarea(viewOnly)/ChipSwitch/ChipTag/FieldDivider/Label — so it reads as the real editor panel, fed example data (read-only, full opacity via readOnly/viewOnly, not greyed). Slider stays minimal (no emcn equivalent) but on app tokens. Props API and embedded/standalone modes unchanged.
* refactor(docs): render the block-reference hero through the shared View
Retires the hand-rolled BlockCard (a parallel reimplementation of WorkflowBlockView) and the BlockDisplaySpec data model. Each block hero is now a single-block PreviewWorkflow (block-display-workflows.ts) rendered through the same toReactFlowElements -> DocsBlockNode -> WorkflowBlockView pipeline as the diagrams, mounted in a minimal fitView ReactFlow (maxZoom 1.3, no canvas chrome). A single block can no longer drift from the canvas.
* fix(docs): define sim's type scale + align the preview inspector to the editor
Docs Tailwind v4 never defined sim's custom font sizes (text-small/caption/md/micro), so emcn components (Label, Badge, the shared views) fell back to inherited sizes — the inspector labels rendered huge. Adds the type scale to the docs @theme. Also aligns the inspector header to the real editor panel (surface-4 bar, size-[18px] rounded-sm icon, text-sm name) and removes the Connections section (and its now-dead prop/wiring).
* fix(docs): inspector shows the full field list + dragged positions persist
Inspector: shows the block type's full field list (from the reference data) with the example's values overlaid, so it reads like the editor panel instead of only the canvas summary rows. Drag: selecting another block no longer relayouts the canvas — node positions the viewer dragged are preserved across highlight/selection changes (only a different workflow relayouts).
* feat(docs): highlight <> references + env vars; hide Ask AI over the lightbox; respace blocks
Inspector text fields render the value with <...> block references and {{...}} environment variables highlighted in brand-secondary (a lean read-only port of the editor's formatDisplayText), in the canonical chip field chrome. The floating Ask AI widget is hidden while a preview lightbox is open. Plus the example-data respacing so the editor-faithful Error row no longer makes stacked blocks overlap.
* fix(docs): make per-type field templates match the real block registry
Audited every block type's field list (the source the inspector + block-reference heroes render) against apps/sim/blocks/blocks/*. Corrected drift to the registry's default-visible fields, titles, and order: agent gains Temperature; router gains Model; wait gains Async; schedule rewritten (default is Daily, not minutes); webhook_trigger expanded to its real default-visible set; human_in_the_loop notification title fixed. Provider-credential and advanced-mode fields stay hidden, matching the editor. Canvas diagrams keep their clean curated rows; the inspector now shows the full, real field list per the chosen clean-canvas/full-inspector split.
* improvement(docs): taller default preview height so respaced diagrams aren't shrunk
Bumps the default WorkflowPreview height 260->300 (the respaced, editor-faithful blocks are taller, so fitView was shrinking diagrams that relied on the default). The tall how-it-runs routing diagram gets 400.
* improvement(docs): zoomable inline preview + taller default + themed controls
The inline preview is now zoomable outside the lightbox: adds react-flow zoom/fit Controls (themed to the dark canvas chrome) and enables pinch-zoom, while keeping scroll-zoom off so the page still scrolls over the diagram. Pan-drag and click-block-to-inspect already worked. Default height 300->340.
* improvement(docs): click canvas to expand; click empty lightbox to deselect
Clicking the inline preview canvas opens the full lightbox; clicking empty space in the lightbox clears the selection, matching the real editor.
* improvement(docs): reveal inline zoom controls on hover only
The always-visible zoom controls felt heavy on the inline preview; they now fade in on hover (matching the expand button) and stay visible in the lightbox.
* improvement(docs): drop zoom controls on the inline preview
Inline preview keeps pinch-zoom, pan, drag, and click-to-expand; zoom buttons stay in the lightbox only.
* improvement(docs): remove zoom controls from the lightbox too
Both previews zoom via scroll/pinch and pan via drag; no on-canvas zoom buttons. Drops the Controls import and its theming CSS.
* improvement(docs): match the real canvas — flat background + editor edge geometry
Closes the last faithfulness gaps the audit found: removes the dot grid (the real editor hides its background — flat bg), aligns PreviewEdge to the editor's smoothstep math (borderRadius 8, offset 30) and 2px stroke (default + error edges), the selection ring to 1.75px, and minZoom to 0.1. Structural parity (blocks/handles/containers/colors/tokens) was already shared. Kept PreviewEdge rather than swapping to WorkflowEdgeView, which would clobber the docs-only highlight/dim/animate for no visual gain.
* improvement(docs): rebrand the docs assistant as 'Ask Sim', styled like the real chat input
Renames the floating assistant from 'Ask AI' to 'Ask Sim' (matching the platform's voice — you talk to Sim) and restyles the composer to mirror the home chat input: a rounded-2xl bordered field with the toolbar inside, and the same 28px circular send/stop button (the home's exact active/disabled colors + white/black arrow). Updates the lightbox hide-selector to the new label.
* improvement(docs): match Ask Sim message styling to the mothership chat
Aligns the user bubble (rounded-[16px] surface-5, text-base/primary, leading-23, max-w-85%) and the assistant markdown (text-base, 600 headings/strong, text-primary dashed-underline links, surface-5 code blocks) to the real mothership chat's user-message + chat-content treatment, instead of the prior generic text-sm rendering. The composer already mirrors the home user-input (rounded-2xl field + 28px circular send button).
* improvement(docs): compact single-row Ask Sim composer
The two-row layout left a tall dead gap (the docs widget has no toolbar buttons to fill the second row). The composer is now a single row — textarea with the circular send button inline — so it sits at the natural input height.
* fix(docs): pass the router Context value to the shared view
DocsBlockNode never set routerContextValue, so the view (which renders the router's Context row from that prop, not from rows) showed a blank Context even when the preview data authored a value like <start.input>. Extract it from the block's Context row and pass it through.
* fix(docs): don't apply a block-type field template that doesn't match the block
inspectorFieldsFor keyed the full field template purely off block.type, but some types are reused across roles (a table action block vs the table trigger, a webhook trigger vs the webhook action), so the wrong template was applied. Only use the template when the block's authored rows are actually a subset of it; otherwise fall back to the block's own rows.
* fix(docs): connect preview edges to subflow container handles
toReactFlowElements hardcoded targetHandle to 'target' and defaulted source handles to 'source', but Loop/Parallel containers (SubflowNodeView) expose a 'loop-end-source'/'parallel-end-source' output handle and a left input handle with no id. Edges into and out of containers therefore failed to connect. Resolve each edge end to the block's real handle based on whether it's a container.
* fix(docs): don't expand the inspector template for blocks with no rows
block.rows.every(...) is vacuously true for an empty rows array, so a block defined only by branches (e.g. a router in ROUTING_WORKFLOW) inherited the type template's invented field defaults. Require non-empty authored rows before applying the template.
* fix(docs): render blank branch/router-context values as '-' like the editor
The editor maps condition/router branch values and the router Context through getDisplayValue, which renders '-' for a blank value. DocsBlockNode mapped them to an empty string, so else branches and unset routes looked blank instead of matching the editor. Mirror getDisplayValue's empty-value handling.
* fix(docs): show '-' for blank inspector branch values, matching the canvas
inspectorFieldsFor passed raw branch.value into the lightbox branch fields, so an unset else route read blank in the inspector while DocsBlockNode (and the editor's getDisplayValue) render '-' on the canvas. Normalize the same way; drop the now-redundant placeholder.
|
||
|
|
48c1b453df |
feat(integrations): extend ElevenLabs, Google Drive, Firecrawl, Pinecone, Resend, and S3 tool depth (#5270)
* feat(firecrawl): add crawl status/cancel, batch scrape + status, extract status, credit usage tools * feat(resend): add audiences, broadcasts, and cancel-email tools * feat(pinecone): add delete/update vectors, index, and stats tools * feat(google-drive): add revisions, comments, and export tools * feat(elevenlabs): add voices, settings, models, user, sound-effects, speech-to-speech, audio-isolation tools * feat(s3): add bucket CRUD, head-object, presigned-url, and batch-delete tools * chore(api-validation): bump route baseline to 873 for wave-3 internal tool routes (s3, elevenlabs, google_drive export) * docs(integrations): regenerate docs + catalog for wave-3 tools * fix(integrations): audit fixes for wave-3 - pinecone: read camelCase vectorType/deletionProtection (with snake_case fallback) so list_indexes/describe_index populate them; make describe_index_stats casing defensive - google-drive: URL-encode fileId in the export route - remove extraneous inline/section-divider comments across new blocks/tools; convert type docs to TSDoc * fix(integrations): address review — elevenlabs settings bleed, batch-scrape job-id guard, s3 head-object existence - elevenlabs: select stability/similarityBoost by operation so a stale edit-settings value can't bleed into a TTS call - firecrawl: fail fast with a clear error when batch scrape returns no job id (avoids a misleading polling timeout) - s3: head_object on a missing key now returns exists:false instead of a generic failure * fix(s3): allow exists:false in head-object response contract (schema must match the missing-object output) * fix(pinecone): guard JSON.parse of ids/filter/values/sparseValues/setMetadata Malformed JSON-string input now throws a clear '<field> must be valid JSON' error via a shared parseJsonParam helper instead of crashing the request body builder. * fix(pinecone): enforce mutual exclusivity of ids/deleteAll/filter in delete_vectors Pinecone treats these delete selectors as mutually exclusive; the tool now requires exactly one and throws a clear error otherwise, instead of sending a conflicting body. * fix(integrations): I/O fidelity vs API docs (wave-3 audit) - pinecone: normalize describe_index_stats per-namespace vector_count -> vectorCount - firecrawl: remove phantom 'sources' from extract_status, add real creditsUsed/tokensUsed; expose batch_scrape maxConcurrency/ignoreInvalidURLs - google-drive: drop undocumented supportsAllDrives from the files.export URL - elevenlabs: add next_page_token input to list_voices (fixes pagination dead-end) - resend: surface segment_id on get_broadcast; declare replyTo + segment_id in block outputs |
||
|
|
69e3550a72 |
feat(integrations): extend Telegram, Outlook, and Notion tool depth (#5265)
* feat(telegram): add edit, forward, copy, location, contact, poll, pin, reaction, chat-action, and chat-info tools * feat(outlook): add reply, reply-all, folders, attachments, search, and message-update tools * feat(notion): add block children CRUD, comments, and users tools (v1 + v2) * docs(integrations): regenerate docs + catalog for telegram, outlook, notion tools * fix(notion): guard pageSize coercion with Number.isFinite so non-numeric input isn't forwarded as NaN * fix(telegram): normalize send_poll options (array, JSON string, or newlines) so json-typed input can't crash on .map * fix(integrations): harden outlook search quoting, notion archive default, and telegram poll options - outlook: strip embedded double quotes from the $search term so they can't break the quoted KQL query - notion: default the update_block Archive dropdown to 'Leave unchanged' so updates don't send an unintended archived:false restore flag - telegram: pass raw poll options through to the tool's normalizePollOptions (handles array/JSON-string/newlines) so the block layer no longer mishandles JSON-string input * fix(integrations): normalize outlook categories input and clamp notion pageSize - outlook: normalize update_message categories (array, JSON string, or comma/newline) so a JSON-string value isn't silently dropped - notion: clamp pageSize to Notion's 1-100 range (truncated) so out-of-range values don't hit an API error * fix(outlook): pass raw categories to the tool's normalizeCategories so the block handles JSON-string input too * fix(outlook): allow clearing message categories by passing an empty array An explicit empty array now sends categories:[] to clear all labels, a non-empty value replaces, and an absent/empty value leaves them untouched — matching the documented replace semantics. * fix(outlook): only clear categories on an explicit empty array, not a delimiter-only string A string that normalizes to no categories (e.g. just commas) is now a no-op rather than clearing all labels; clearing requires an explicit empty array. * fix(notion): clamp page_size to 1-100 at the tool layer for list comments/users and block children Adds a shared clampNotionPageSize helper so the agent-direct path is bounded to Notion's range, not just the block path. * fix(outlook): use consistent set/replace semantics for message categories Categories are replaced with the provided non-empty list and left unchanged when empty, consistent across the block and agent paths. Drops the ambiguous empty-array clear (clearing all categories isn't expressible unambiguously from the comma-separated field) and updates the description to match. |
||
|
|
f5f87de96d |
fix(emcn): repair app-wide crash and unstyled UI after package extraction (#5258)
Two regressions from moving emcn into @sim/emcn: 1. optimizePackageImports['@sim/emcn'] rewrote barrel imports to direct subpaths, duplicating the toast module so ToastProvider (layout) and useToast (workspace permissions provider) resolved different ToastContext objects — useToast threw 'must be used within <ToastProvider>' on every workspace route. Removed @sim/emcn from optimizePackageImports. 2. emcn's source left apps/sim's Tailwind content globs (and docs' v4 auto-content scope, which excludes node_modules), so utility classes used only inside emcn components stopped generating and components rendered unstyled. Added the package to apps/sim's Tailwind content and a @source to docs. |
||
|
|
bcf6a804f9 |
improvement(emcn): extract design system into shared @sim/emcn package (#5257)
Moves apps/sim/components/emcn into a shared @sim/emcn package consumed directly by apps/sim and apps/docs. cn/keyboard/use-copy-to-clipboard move into the package; all imports become direct @sim/emcn (icons via @sim/emcn/icons, CSS via file path). ChipModal email validation is now prop-driven (quickValidateEmail stays in apps/sim, injected via validate). Docs drops its local chip/chip-dropdown/dropdown-menu mirrors and consumes @sim/emcn. |
||
|
|
d878f15815 |
feat(integrations): extend Airtable, Google Docs, WhatsApp, and Excel tool depth (#5256)
* feat(airtable): add delete and upsert record tools * feat(google-docs): add batchUpdate text, table, image, and style tools * feat(whatsapp): add template, media, interactive, reaction, and mark-read tools * feat(microsoft-excel): add clear, format, create-table, sort, and delete-worksheet tools * fix(integrations): address review feedback - google-docs: use camelCase fontSize field mask; normalize string booleans for bold/italic/underline and matchCase - microsoft-excel: escape OData single quotes in worksheet/table keys; validate range for clear/format - airtable: enforce batch limits (delete <=10 ids, upsert <=10 records and 1-3 merge fields) with clear errors * fix(integrations): address round-2 review - airtable: coerce upsert typecast as string-aware boolean (string "false" no longer truthy) - microsoft-excel: format_range surfaces precise partial-state error when fill PATCH fails after font (no atomic font+fill endpoint in Graph) * fix(whatsapp): treat 2xx mark-as-read as success unless body says success:false * fix(integrations): build fix, doc accuracy, and comment cleanup - google-docs: align manualDocumentId condition with the document selector so the documentId canonical group has matching conditions (fixes canonical-pair block test / build) - microsoft-excel: describe fill/font color as hex code only (Graph does not reliably accept named colors) - remove verbose explanatory inline comments from new tools (keep idiomatic section dividers) - regenerate integration docs + integrations.json catalog from the block registry * fix(integrations): harden delete-record id coercion and excel sort-column validation - airtable: coerce recordIds entries via String() so numeric JSON values don't crash on .trim() - microsoft-excel: drop the silent sortColumn default to 0 so invalid input surfaces the tool's clear validation error (both v1 and v2 blocks) * fix(airtable): coerce upsert fieldsToMergeOn entries via String() to handle non-string values |