Commit Graph
717 Commits
Author SHA1 Message Date
Waleed 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.
2026-07-07 12:53:50 -07:00
Waleed 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.
2026-07-07 12:14:38 -07:00
Vikhyath Mondreti 24ebba9acc chore(credential-sets): cleanup feature (#5460) 2026-07-06 18:49:06 -07:00
Waleed 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.
2026-07-06 18:19:52 -07:00
Waleed 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.
2026-07-06 17:26:44 -07:00
Waleed 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.
2026-07-06 16:07:03 -07:00
Waleed 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)
2026-07-06 15:41:14 -07:00
Will Chen 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)
2026-07-02 18:35:59 -07:00
Waleed 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.
2026-07-02 11:58:30 -07:00
Waleed 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.
2026-07-02 11:04:17 -07:00
Waleed e7c9a67194 fix(algolia): tighten tools.config, add geo/facet search + task-status tool; icon/color tweaks (#5356)
* fix(algolia): fix serialization-time param mutation, add geo/facet search, task status tool

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* fix(algolia): correct batch_operations body requirement wording

Verified against Algolia's actual batchWriteParams schema (specs/common/schemas/Batch.yml):
body is a required property on every batch request item, including
index-level delete/clear actions — it isn't omittable. The tool's
param description previously said to omit it; corrected to say use an
empty object instead.
2026-07-02 10:02:36 -07:00
Waleed 507cee1187 fix(integrations): repair corrupt icons, backfill missing block metas, restore scroll on back-nav (#5342)
* fix(integrations): repair corrupt icons, backfill missing block metas, restore scroll on back-nav

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

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

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

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

Greptile review: iconNameAt only matched 'export function', so a malformed path inside an 'export const XxxIcon = (...)' arrow-function icon would be misattributed to the preceding function-declared icon. Match both forms (mirrors check-bare-icons indexIconBodies).
2026-07-01 16:58:45 -07:00
Waleed 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).
2026-06-30 17:18:57 -07:00
Waleed 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
2026-06-30 16:59:42 -07:00
Waleed 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.
2026-06-30 11:39:11 -07:00
Waleed 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.
2026-06-29 20:27:33 -07:00
Waleed 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
2026-06-29 15:59:14 -07:00
Waleed 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.
2026-06-29 13:02:29 -07:00
Waleed 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.
2026-06-28 23:52:47 -07:00
Waleed 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.
2026-06-28 22:50:48 -07:00
Waleed 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
2026-06-28 22:42:32 -07:00
Vikhyath Mondreti c59631698f chore(deploy): remove deploy as a2a (#5255)
* chore(deploy): remove a2a

* add block
2026-06-28 20:01:24 -07:00
Waleed 66315f19e7 improvement(docs): flatten the academy learn/chapters panels (#5253)
* improvement(docs): flatten the academy learn/chapters panels

The "What you will learn" and "Chapters" panels were filled, bordered
cards — the only boxed elements on the page. The docs design system is
explicitly flat: global.css strips fumadocs cards/callouts/card-grids to
transparent/divider-based, and the right-rail "On this page" TOC is small,
muted, and borderless.

- WhatYouWillLearn (inline): flat divider list like the FAQ, with a small
  panel label at the app's text scale instead of a page-h2-scale title
- VideoChapters (right rail): borderless, matching the TOC — small muted
  label + flat hover rows, no card chrome

* improvement(docs): drop the repeated per-row play icon from the chapters list

The CirclePlay glyph repeated on every chapter row read as noise — a column
of identical icons down the rail. The "On this page" TOC this list mirrors
has no per-row icons; the timestamps already signal video chapters and the
hover highlight signals they're seekable. Rows are now text + time only.

* improvement(docs): drop the under-label rule on the learn callout

A full-width rule under the small "What you will learn" label read as an
awkward heading underline and blurred into the inter-item dividers. The label
is now a quiet muted marker (matching the Chapters label and the TOC heading),
with dividers only between items — so it never competes with the bold item
titles or looks like an underlined heading.
2026-06-28 14:35:39 -07:00
Waleed 3143a15dde feat(uptimerobot): add UptimeRobot v3 integration (#5229)
* feat(uptimerobot): add UptimeRobot v3 integration

- 24 tools across monitors, incidents, maintenance windows, alert contacts,
  public status pages, and account (UptimeRobot v3 REST API, Bearer auth)
- Block with operation-scoped subBlocks, status-page logo/icon file uploads
  via internal multipart routes, and BlockMeta templates + skills
- Registered tools/block, added icon, generated docs
- Updated add-integration/add-block/validate-integration docs links to /integrations

* fix(uptimerobot): address review — heartbeat URL, file/JSON edge cases

- Block: URL is not required for HEARTBEAT monitors (no URL)
- buildMonitorBody: throw on malformed assignedAlertContacts/customHttpHeaders
  JSON instead of silently dropping the field
- PSP route: error (400) when a supplied logo/icon cannot be resolved to a
  stored file instead of silently omitting the image
- PSP route: guard success-path JSON parsing; return a controlled 502 on a
  non-JSON provider response instead of an uncaught 500

* fix(uptimerobot): spec-conformance audit fixes

- pause/start monitor: send Content-Type: application/json (v3 spec requires it
  on these POSTs even with an empty body)
- update maintenance window: drop autoAddMonitors (not in UpdateMaintenanceWindowDto);
  gate the block field to create only

* fix(uptimerobot): rename monitor timeout param to avoid reserved name

The tool runner treats a top-level `timeout` param as the outbound HTTP-client
timeout (ms), so a monitor check-timeout of e.g. 30s would abort the API call in
30ms. Rename the input to `checkTimeout` (block subBlock, tool params, inputs,
numeric coercion) and map it to the API body's `timeout` key in buildMonitorBody.

* fix(uptimerobot): reject empty/non-object PSP responses

A successful PSP create/update must return the PspDto object; an empty or
non-object body now returns a controlled 502 instead of mapping a phantom
status page (id: 0, empty name, null images) back to the workflow.

* fix(uptimerobot): validate core PSP fields before mapping

Reject successful PSP responses that lack a positive numeric id and non-empty
friendlyName (a {} or metadata envelope) with a controlled 502, instead of
mapping a phantom status page.
2026-06-26 18:38:50 -07:00
Waleed 35acc42d2b feat(downdetector): add Downdetector outage-monitoring integration (#5228) 2026-06-26 18:25:31 -07:00
Waleed c7eda5b217 feat(rich-editor): rich markdown field + @ mentions for skill & deploy modals (#5215)
* feat(rich-editor): rich markdown field + @ mentions for skill & deploy modals

- Add controlled, file-less RichMarkdownField (sibling of the file editor) used for
  skill Content and deploy version descriptions; placeholder/typography match chip fields
- Add @-mention menu (TipTap suggestion) inserting portable [label](sim:kind/id) links;
  wired into the field and the file viewer via a shared useEditorMentions hook
- Extract a shared suggestion-popup renderer + menu chrome (slash + mention)
- Fix false dirty-on-open: normalize the editor's dirty baseline to canonical markdown
- Always show the deployment version number (v3 · name) so named versions keep a short ref
- Skill import: drop the paste box (Create-tab editor auto-destructures a pasted SKILL.md),
  reorder GitHub → Upload

* fix(rich-editor): address review feedback on modal field

- RichMarkdownField reports the original value when the doc matches its canonical
  form, so a non-canonical input never reads as a false unsaved change (skill +
  version description modals)
- Add sim: mention link navigation (Cmd/Ctrl-click) to the modal field
- versions: keep the v{n} fallback as the rename guard/seed so re-submitting the
  displayed token is a no-op (no redundant "v3 · v3"); document the clear-name no-op
- Clarify the lazy query-gating comment in useMarkdownMentions

* fix(skills): re-seed Content editor when initialValues changes

Bump the field's remount key in the reset guard so the seed-once rich editor
re-seeds when content is reset from a changed initialValues (same skill id keeps
the React key otherwise stable), keeping the editor and saved value in sync.

* feat(rich-editor): render mentions as icon chips + menu/limit polish

- Render @ mentions as an inline chip node (entity icon + label) instead of a
  blue link; still serializes to the portable [label](sim:kind/id) markdown so
  it round-trips and stays agent-readable (shared mentionIcon resolver)
- Cap the mention/slash menu height + width and scroll it, matching the chat menu
- Give the version description editor more height; lift the 2000-char limit to a
  high anti-abuse cap (client + contract) and drop the visible counter

* fix(rich-editor): make suggestion menus scrollable inside modals

- Mount the slash/@ menu popup inside the host dialog (when present) instead of
  document.body: Radix's scroll-lock blocks wheel events outside the dialog
  subtree, so a body-level popup couldn't scroll in a modal. position:fixed keeps
  it viewport-positioned (the modal centers via flex, no transform) so it isn't clipped
- Fix the invalid max-w arbitrary value (calc needs spaces) that left the menu uncapped
- Match the version-description editor's dynamic-import loading height to the field
  so the modal doesn't grow when the chunk loads

* fix(rich-editor): escape bracketed mention labels + disable images in field editors

- Escape/unescape `[`/`]` in mention labels so an entity named e.g. `data[1].csv`
  round-trips into a chip instead of degrading to a plain link
- Hide the `/Image` command where image upload isn't wired (the skill + version
  description field editors), so images can't be inserted there; the file viewer
  keeps image support

* fix(rich-editor): keep suggestion keyboard nav working after async items load

The suggestion plugin captures the list's onKeyDown handle via ReactRenderer.ref
once at mount. The mention list's items arrive asynchronously from the workspace
store, so the captured handle closed over an empty `flat` and returned false for
arrow/enter — letting the editor move the caret instead of navigating the menu.
Read live values through a ref so the mount-time handle always sees current
items/activeIndex. Hardened the slash list the same way.

* test(rich-editor): cover suggestion keyboard nav through ReactRenderer; drop inline comments

Adds a test that drives the real ReactRenderer path the suggestion plugin uses:
the captured onKeyDown handle returns false while the store is empty and true
once async workspace items land, and arrow+enter select the right item. Removes
the explanatory inline comments from the two imperative handles.

* fix(rich-editor): suggestion menus keep arrow keys when a divider is adjacent

The leaf-selection keymap (ArrowUp/Down selects an adjacent divider/image) runs at
priority 1000, above the suggestion plugins, so it stole ArrowDown to select the
next horizontal rule instead of moving the open @/ menu selection. It now yields
while a mention or slash menu is active, detected via the plugins' exported keys.

* feat(rich-editor): Tab accepts a suggestion; unify list keyboard nav; match chip styling

- Extract useSuggestionKeyboard: one hook owns the @/ menus' active-row state,
  scroll-into-view, and arrow/enter/tab handling (removes the duplication between
  the two list components)
- Tab now accepts the active item like Enter, matching the chat composer
- Render the mention chip like the chat input's mention token: borderless inline
  icon + label (no pill), 12px icon with brand color via getBareIconStyle, so the
  styling is consistent across surfaces

* fix(rich-editor): harden editor edge cases found in full audit

- Skill paste: only auto-destructure on a real YAML name key, so a stray `---`
  break or heading snippet no longer overwrites all three fields (parseSkillMarkdown
  reports nameFromFrontmatter)
- Skill modal: reset by skill id, not object identity, so a background refetch of
  the open skill can't clobber in-progress edits
- Field editor: claim Mod+K (inline link editor wins over global search) and
  swallow file drops so the browser doesn't navigate away from the modal
- File editor: swallow non-image file drops (same navigation guard)
- Frontmatter: a leading `---` thematic break (e.g. a changelog) is no longer
  mistaken for frontmatter and hidden from the editor
- Mention chip: renderText emits the portable link so copying a chip into a
  plain-text target (e.g. chat) pastes back as a mention
- Suggestion nav: clamp a one-frame stale active index on Enter/Tab

* style(rich-editor): selected link reads as normal text, not standout blue

Follows the standard MD-editor convention (Linear, Slack): a highlighted link
takes the primary text color so the selection stays legible, instead of keeping
its blue on the selection highlight. Scoped to selected links only — no effect on
unselected links, regular text, the selection background, or any other surface.

* fix(rich-editor): guard async suggestion + generate lifecycles against teardown

- Suggestion onStart can fire after the editor is destroyed (the update awaits
  items()), throwing on its now-gone view/storage — e.g. a modal closing while the
  menu opens. Bail when the editor is destroyed; optional-chain mention storage.
  This also removes the unhandled rejections the headless keymap test surfaced.
- Generate version description: thread an AbortSignal so closing the modal
  mid-stream aborts the diff fetches + SSE read instead of streaming into a gone
  component.

* refactor(rich-editor): fold inline comments into TSDoc; display-only chip; polish

- Convert the editor's inline `//` comments to TSDoc on the nearest declaration and
  drop the self-explanatory ones (no logic change)
- Mention chip is now display-only (icon + label), matching the chat input exactly:
  removes select-none (so a range selection highlights the label), the cursor-pointer
  over-promise, and the cmd-click nav that could route away from a modal mid-edit
- Don't log a deliberate generate-abort as an error
- Selected strike-through text reads in the primary color so the selection is uniform

* fix(rich-editor): clean selection for the mention chip

The chip is an inline atom, so a range selection now highlights it as a whole unit
(the prior select-none left it an un-highlighted gap). A direct click selects it
with a subtle fill instead of the block-leaf outline ring meant for dividers/images.

* feat(rich-editor): Cmd/Ctrl-click a mention to its resource in the file viewer

Threads a `navigable` flag through the mention storage: the file viewer opts in so
a chip routes to its file/table/workflow/etc., while modal fields stay inert so a
click can't navigate away from an unsaved edit. Styling is identical either way.

* fix(rich-editor): icon fallback for removed integrations; smoother divider nav

- A mention to a since-removed integration falls back to a generic icon so the chip
  is never icon-less (indistinguishable from prose)
- Arrowing from a selected divider/image to an adjacent one selects it directly
  instead of stopping on the gap cursor between them, so stepping through a run of
  dividers is one press each

* fix(rich-editor): integration mentions are display-only; robust chip selection

- An integration mention's id is a block type (gmail_v2), not a routable resource —
  /integrations/[block] expects a slug and a type maps to zero-or-many credentials —
  so it no longer links to a 404. The chip only shows a pointer/navigates on kinds
  that resolve to a real page.
- Scope the block-leaf selection ring off the mention chip robustly (covers a
  node-view wrapper via :has), so a selected chip shows a subtle fill, not the outline.

* feat(icypeas): update brand icon and bgColor; regenerate integration docs

* fix(rich-editor): remove the misfiring chip selection fill (full-width gray band)

The :has fill could paint a full-width band; drop it. A selected chip just skips the
block-leaf outline ring and uses the same native text-selection highlight as the prose.

* feat(rich-editor): show an "Uploading…" toast while an image uploads

A persistent progress toast appears per image during upload and is dismissed once
it settles, when the upload hook's "Uploaded"/"Failed" toast takes over — previously
nothing showed until the upload finished.

* refactor(rich-editor): drop inline comments (TSDoc on the declarations instead)

Fold the image-upload toast note into the insert function's TSDoc and remove the
remaining inline // comments.

* refactor(rich-editor): cleanup + simplify pass over the markdown editor

- Delete dead parseSimHref (+ barrel/test); mentions parse via the node tokenizer
- Extract serializeMarkdownDocument — one canonical serialize pipeline shared by the
  dirty-check baseline and the round-trip-safety probe (was inlined in both)
- Extract selectLeafAcross — shared tail of the two arrow leaf-selection handlers
- Reset the suggestion active-row during render (prevX idiom) instead of an effect
- Inline the skill modal's trivial hasChanges (drop the useMemo)
- image.tsx: cn() over a template-literal className

* refactor(rich-editor): share the suggestion-list shell and link-URL editor

- Extract SuggestionList: the grouped-list surface, empty state, listbox/option a11y
  structure, and active-row/hover/select wiring shared by the @ and / menus. Each menu
  keeps only its own grouping + itemKey/renderItem.
- Extract link-editing (LinkUrlInput + applyLink): the inline link field and the
  normalize→extendMarkRange→set/unset commit logic, shared by the bubble menu and the
  link hover card.

* improvement(settings): align access-control detail UI + nav-driven docs link

- Move permission-group Save/Discard into the detail header (matching
  secrets/whitelabeling) and delete the one-off sticky 'Unsaved changes' bar
- Convert the Platform and Blocks config tabs to SettingsSection (drop the
  custom multi-column masonry + hand-rolled section labels); add an optional
  far-right action slot to SettingsSection for the per-section Select All
- Replace the file-share auth-mode checkboxes with a multi-select ChipDropdown
- Normalize per-tab spacing to gap-7, align the expand-chevron token to
  --text-icon, and match the list-row arrow size to the integrations precedent
- Add a nav docsLink surfaced as a header 'Docs' ChipLink by SettingsPanel,
  wired for the six enterprise settings pages

* feat(rich-editor): copy-link button shows a checkmark on copy

Use the shared useCopyToClipboard hook so the link hover card's Copy button swaps to a
Check for ~2s after copying, matching the rest of the platform.

* fix(rich-editor): RichMarkdownField falls back to raw text for lossy markdown

Mirror the file editor's safety gate: decide once from the initial value via
isRoundTripSafe — round-trip-safe content opens in the WYSIWYG editor, while lossy
markdown (raw HTML, footnotes, comments) edits as raw text, so an edit can't silently
drop those constructs.

* feat(rich-editor): divider/leaf editing — backspace, select-all, gap cursor

- Backspace at the start of an empty block whose previous sibling is a divider/image removes the
  blank line (instead of deleting the leaf) and selects the divider above; a non-empty block selects
  the leaf so a second Backspace deletes it (highlight-before-delete).
- Select-all (and any range selection) now visibly highlights dividers/images, which the native text
  highlight skips because leaves carry no text — via a decoration that paints a selection band.
- The gap cursor between two adjacent leaves no longer draws its stray caret (matching Linear); the
  position stays functional. Leading/trailing gap cursors keep their caret.
- Unit tests for the backspace + select-all behavior.

* refactor(rich-editor): decouple headless bundle, fix linked-image round-trip, a11y

- Split mention-node into the schema-only `MarkdownMention` (mention-node.ts, no React/registry)
  and the live `MentionChip` node view (mention-chip.tsx); move the live factory to
  editor-extensions.ts and inject node views via DI. The headless round-trip path
  (markdown-parse/normalize-content/round-trip-safety) no longer pulls the 269-block registry —
  it now bundles for the browser with zero node-builtin deps.
- A sized + linked image serializes as `[![alt](src)](href)` (dropping the unrepresentable size)
  instead of `[<img>](href)`, which the tokenizer can't reparse — the link is preserved, no silent
  data loss. Also escape the href title symmetrically.
- Wire the suggestion menus as an ARIA combobox: while open, the editor gets
  aria-haspopup/expanded/controls and an aria-activedescendant tracking the active option, so screen
  readers announce it; cleared on close. Empty state is a role=status live region.

* fix(rich-editor): exempt an active @-mention query from the per-group cap

The per-group MAX_PER_GROUP limit is meant to keep the unfiltered menu from flooding; applying it
while a query is active hid matches past the eighth in a category, so search couldn't reach them.
Cap only when there's no query. Adds a regression test (12 matches shown when searching).

* fix(rich-editor): mention icon fallback + typed sim-link input rule

- mentionIcon never returns undefined: an empty/unrecognized kind (schema default '', or a future
  kind on a sim: link) falls back to a generic icon instead of crashing the chip's render. Adds tests.
- Add a mention input rule so typing `[label](sim:kind/id)` becomes a chip on the closing paren —
  matching the paste/load path (the tokenizer), which previously left typed syntax as literal text.
  A plain InputRule (full-range replace) is used; nodeInputRule would keep the surrounding brackets.

* fix(rich-editor): raw-fallback paste hook + bound the filtered mention list

- RawMarkdownField now honors onPasteText (e.g. skill SKILL.md destructuring), so a full-document
  paste is intercepted in the raw fallback too, not only the WYSIWYG path.
- Bound the @-mention list while filtering (MAX_WHEN_FILTERED) so lifting the per-group cap for search
  can't render thousands of rows in the non-virtualized menu on a broad query; search still reaches
  deep matches well before the bound. Adds a test.
- Tighten an extensions.ts doc comment (the headless path omits the registry + node-view construction,
  not React itself).
2026-06-26 17:44:24 -07:00
Waleed 954de0559b improvement(docs): align components with the platform design system (#5227)
* improvement(docs): align components with the platform design system

Bring the docs app's chrome in line with the main Sim design system,
validated against the canonical emcn source in apps/sim.

- ask-ai: fix undefined tokens (--text-base x6, --text-link) that broke
  the send-button fill and link/text colors; send button now matches the
  canonical primary fill (text-primary/text-inverse, dark:bg-white); use
  --shadow-medium and chip gap rhythm
- not-found: replace the hand-rolled brand pill with <ChipLink variant='brand'>
  and swap fumadocs tokens for platform tokens
- search-trigger: compose the exported chip chrome constants instead of
  re-spelling them (single source of truth)
- what-you-will-learn, video-chapters: fumadocs fd-* tokens -> platform tokens
- workflow-preview: add --wp-highlight token; route the #33b4ff highlight,
  #ef4444 error dots, and toggle/slider green through tokens
- video-placeholder: tokenize the status pill (bespoke illustration art
  intentionally left as-is)

dropdown-menu, faq, theme-toggle, and page-type-badge were deliberately
left at their canonical values (14px row icons, rounded-md badge) after
validation showed those match the platform, not the chip-pill, standard.

* improvement(docs): neutral primary chip for nav CTA + fix cluster spacing

Align the docs navbar with the main app, which reserves green for
accents/status and uses a neutral high-contrast CTA in nav.

- add a canonical `primary` chip variant (inverse fill: dark in light
  mode, white in dark mode), mirroring the emcn chip's primary action
- "Get started" and the 404 "Go home" now use variant='primary' instead
  of the green brand surface
- retire the now-unused `brand` chip variant (no parallel path left behind)
- fix navbar right-cluster spacing: gap-2 to match the landing navbar and
  drop the asymmetric ml-1 on the CTA
2026-06-26 17:24:09 -07:00
Waleed a1d5870681 feat(settings): unify all settings pages under a shared SettingsPanel layout (#5219)
* feat(settings): add SettingsPanel layout with nav-driven page titles

Introduce a SettingsPanel scaffold that owns the standard settings chrome
(fixed header bar with right-aligned actions, scroll region, centered content
column) and renders a consistent page title + description pulled from the
active section's navigation metadata. Adds a description to every nav item and
a SettingsSectionProvider in the shell so the title is required-by-default
without per-page wiring.

Migrate the account/subscription pages (general, secrets, teammates,
team-management, billing) to SettingsPanel, removing their hand-rolled shells
and title blocks.

* feat(settings): migrate all settings pages to SettingsPanel + bake in search

Extend SettingsPanel with a `search` prop (canonical search field with
optional anti-autofill hardening) so the repeated per-page search input is
owned by the layout. Migrate every settings page — account, subscription,
tools, system, enterprise, and superuser — onto SettingsPanel so each renders
a consistent nav-driven title + description, header actions, and search with
zero hand-rolled shell. Drill-down detail sub-views (MCP server, workflow MCP
server, credential set, permission group) keep their own back-button chrome.

Normalize all nav descriptions to a consistent voice and length.

* refactor(settings): drop unnecessary search anti-autofill; document SettingsPanel

Remove the preventAutofill prop from SettingsPanel — the shared search field
no longer needs the read-only-until-focus hack, so secrets uses the plain
search like every other page. Pre-existing honeypot inputs are untouched.

Add .claude/rules/sim-settings-pages.md (auto-scoped to settings pages) and a
settings-page skill documenting the SettingsPanel convention + add/audit
procedure.

* refactor(settings): extract SettingsEmptyState + RowActionsMenu, dedupe usages

Encapsulate two more repeated settings patterns into shared components:
- SettingsEmptyState — the muted empty/no-results/gate message (fill | inline),
  replacing ~42 hand-rolled status divs and normalizing stragglers that used
  text-small / text-tertiary back to the canonical text-muted + text-sm.
- RowActionsMenu — the trailing '...' row-actions dropdown, replacing ~11
  per-page DropdownMenu+MoreHorizontal blocks with a props-driven actions list.

Pure presentational refactor: every message, action, handler, disabled, and
destructive flag preserved (verified by diff review). Document both in
.claude/rules/sim-settings-pages.md.

* fix(settings): set autoComplete=off on the shared settings search field

Keeps browsers from offering saved-credential autofill in a filter box — the
lightweight standard guard, distinct from the removed read-only/preventAutofill
machinery. Matters most on the secrets page.

* chore(settings): strip non-TSDoc inline comments from touched files

Remove JSX label comments, section/explanatory // comments, separator block
comments, and copilot's commented-out MCP scaffold across the migrated files,
per the repo's no-non-TSDoc-comments standard. TSDoc and functional directives
kept. Comment-only deletions — no code or behavior changed.

* chore(skills): rename settings-page skill to add-settings-page

Aligns with the verb-prefixed naming of the other skills (add-integration, etc.).

* feat(icons): Thrive icon-only black mark on white

Drop the wordmark from ThriveIcon (tighten viewBox to the logo bounds), set the
mark to pure black; the block bgColor is already white. Synced the docs icon copy.
2026-06-25 18:30:41 -07:00
Will ChenandClaude Opus 4.8 6355c8e699 improvement(docs): Ask AI chat grounded in the docs vector store (#5172)
* docs: Ask AI chat grounded in the docs vector store

Adds an Ask AI chat to the docs site. A floating launcher opens a chat panel
backed by the Vercel AI SDK (OpenAI provider, OPENAI_API_KEY from the
environment). A searchDocs tool runs locale-scoped vector/keyword search over
the existing docs embeddings so answers cite real pages.

The public endpoint is hardened: per-request size/token/step caps, message
sanitization (no client-injected tool results or system prompts), origin
checks, and a per-IP rate limit. Non-English retrieval uses keyword search;
English vector search applies a similarity threshold.

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

* docs: harden Ask AI retrieval + fix stale loading state

- searchDocs: wrap the keyword query in try/catch too, so each retrieval path
  (keyword, vector) is independent best-effort
- ask-ai: gate the loading ellipsis to the in-progress (last) message so older
  empty bubbles don't re-show it while a later request streams

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:32:29 -07:00
Will ChenandClaude Opus 4.8 d20deedf99 improvement(docs): add Academy learning surface (#5213)
Adds the Academy section to the docs: video-first lessons (self-hosted MP4 on
Vercel Blob), organized into Workflows, Agents, Tables, Files, and Knowledge
Bases, each linking back to the reference docs. Lessons use a course layout
(hero video with chapter seek, "what you'll learn", block diagrams).

Docs only — no runtime or auth changes. The content may move to a separate CMS
or its own site (academy.sim.ai) later; the docs are a starting point.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 15:44:14 -07:00
Waleed 371cc94851 feat(thrive): add Thrive Learning integration (47 tools + block) (#5214)
* feat(thrive): add Thrive Learning integration (47 tools + block)

Add a full Thrive Learning (LMS) integration covering the public REST API:
users lifecycle, audiences with members/managers, assignments and enrolments,
completions, content and activity records, CPD, tags, and skills. Uses HTTP
Basic auth (Tenant ID + API key) with a region selector for the v1/v2 hosts.

* fix(thrive): surface malformed JSON errors, drop redundant limit param

- parseThriveArray/parseThriveJsonObject now throw a descriptive error on
  malformed JSON instead of silently sending an empty/omitted value
- additionalFields parse errors are surfaced to the caller
- remove the redundant 'limit' query param (perPage already covers paging and
  the API prioritises perPage over limit) from the five list tools and block

* fix(thrive): use a single status dropdown instead of reused canonicalParamId

The block test forbids reusing a canonicalParamId across different operation
conditions. Replace the two canonical status subblocks (search_users vs
list_enrolments) with one 'status' dropdown whose options are labelled by
context, fixing the canonical-pair validation failures.

* fix(thrive): split status into context-specific user/enrolment dropdowns

Addresses review feedback that one shared status dropdown mixed user
lifecycle values (active/inactive/expired/new) with enrolment values
(archived/complete/open/...). Use separate userStatus and enrolmentStatus
dropdowns (no canonicalParamId) remapped to the tool's 'status' param so each
operation only offers valid options.
2026-06-25 15:26:13 -07:00
Waleed 34d32b97dd feat(salesforce): add Tooling API schema tools (custom field/object) + metadata query (#5209)
* feat(salesforce): add Tooling API schema tools (custom field/object) + metadata query

Add salesforce_create_custom_field, salesforce_update_custom_field,
salesforce_delete_custom_field, salesforce_create_custom_object, and
salesforce_tooling_query so the connector can make schema/metadata changes
(e.g. create a custom field on Account). Previously the integration only did
record CRUD via the REST Data API. Existing `api` OAuth scope covers the
Tooling API; metadata creation is profile-permission gated, so no scope change.

Also: fix Opportunity closeDate being wrongly required on update_opportunity,
make list_reports/list_dashboards descriptions honest (recently-viewed scope),
and document run_report's includeDetails default.

* improvement(salesforce): non-destructive custom field update + align metadata param types

- update_custom_field now does a read-modify-write (GET existing Metadata,
  overlay only provided changes, PATCH) so omitted properties are preserved
  instead of being reset by the Tooling API's full-metadata PATCH; no more
  fabricated label or injected create-time defaults on update
- fieldType is now optional on update (kept from the existing field unless changed)
- widen length/precision/scale/visibleLines param types to number | string to
  match the tool param configs (type: number)

* improvement(salesforce): preserve picklist values and clear stale metadata on field type change

- custom field update now unions provided picklist values with the field's
  existing values instead of replacing the whole valueSet (no data loss)
- when fieldType changes on update, drop the prior type's type-specific
  metadata (length/precision/scale/visibleLines/valueSet/defaultValue/unique/
  externalId) and backfill the new type's required defaults

* improvement(salesforce): scope custom field update to attributes, never the type

update_custom_field no longer changes a field's data type: Salesforce treats a
type change as a separate conversion operation, and a stale forwarded fieldType
could otherwise trigger an unintended destructive migration. The merge keeps the
field's existing type and overlays only the other provided properties, dropping
the type-change/stale-metadata-stripping logic entirely.
2026-06-25 10:37:05 -07:00
Waleed ae4bc05e60 feat(gitlab): add repository, code-review, and CI job tools + validation fixes (#5205)
* feat(gitlab): add repository, code-review, and CI job tools + validation fixes

Expand the GitLab integration with 12 new tools (all host-aware via
getGitLabApiBase, wired through types/index/registry/block):
- Repository: list_repository_tree, get_file, create_file, update_file,
  create_branch, list_branches, list_commits
- Code review: get_merge_request_changes, approve_merge_request
- CI jobs: list_pipeline_jobs, get_job_log, play_job

Validation fixes from /validate-integration:
- Correct the block inputs key (credential -> accessToken) so it matches the
  subBlock id and the params the block reads
- Trim projectId before encoding in all tool request URLs (input hygiene)

/validate-connector and /validate-trigger passed clean against the GitLab REST
API v4 docs — no changes required.

* fix(gitlab): address review feedback + regen docs

- get_merge_request_changes: use the /diffs endpoint (/changes was removed in
  GitLab 18.0); return the diff array + count (drops the MR envelope that /diffs
  no longer provides), fetch max page size in a single call
- create_file/update_file: send explicit `encoding: 'text'` for clarity
- Remove `// =====` separator comments from types.ts (repo convention)
- Regenerate GitLab integration docs + catalog for the 12 new tools
2026-06-24 18:05:40 -07:00
Vikhyath Mondreti 5d7f7e900e improvement(pi): minor improvements to docs (#5192) 2026-06-24 00:04:32 -07:00
Vikhyath Mondreti 8b5d746fe2 improvement(access-controls): ui/ux improvements (#5190)
* improvement(access-controls): ui/ux improvements

* remove unused col
2026-06-23 17:35:41 -07:00
Waleed 77976bcb8b feat(billing): unify upgrade routing with reason context + storage/tables limit emails (#5171)
* feat(billing): unify upgrade routing with reason context + storage/tables limit emails

* fix(billing): re-arm limit-notification dedup on usage drops (prior-usage + decrement)

* fix(billing): isolate per-admin email failures in org limit notifications

* fix(billing): re-arm limit dedup at zero usage and zero prior usage (full clear / wipe-rebuild)

* fix(billing): make storage-decrement notification re-arm only (never send on a shrink)

* fix(billing): resolve recipients before claiming so opt-outs don't burn the dedup threshold

* fix(billing): fire table limit emails on upsert inserts via shared notifyTableRowUsage

* chore(billing): only log a limit email as sent when a recipient actually received it

* chore(billing): match to_jsonb int cast between claim and re-arm for consistency

* fix(billing): notify table limits post-commit so a rolled-back insert never emails or burns the claim

* feat(pi): swap Pi Coding Agent icon to the pi glyph and use a black bgColor

* fix(billing): drop priorUsage re-arm to make dedup a single atomic claim (no duplicate-email race)

* docs(billing): move limit-notification rationale to TSDoc, correct tables warn-once behavior

* docs(db): note limit_notifications dedup is per-account, not per-table

* perf(billing): cut redundant subscription fetches and edge-gate notify to slash DB load

* docs(billing): drop self-explanatory inline comments from the notification path
2026-06-23 10:51:27 -07:00
Vikhyath Mondreti 633391903d feat(pi): add pi coding agent harness (#5178)
* feat(pi): add pi coding agent harness

* formatting

* update docs

* change version num

* guard to prevent prs on error

* update param visibility

* address security concerns

* fix tests

* reorder:
2026-06-22 21:47:53 -07:00
Vikhyath Mondreti 951ad42a23 fix(mcp): missing isDeployed in contract breaking settings, parameter overrides lack of clarity (#5164)
* fix(mcp): missing isDeployed in contract breaking settings, parameter overrides lack of clarity

* address comments

* address ux concern

* address stray 404

* address stale fallback based on live state

* fix

* fix more things

* simplify state mgmt

* add tooltip for server selection
2026-06-21 21:23:42 -07:00
Vikhyath Mondreti 82cb324638 improvement(access-controls): default workspace experience includes all members (#5153)
* improvement(access-controls): default workspace experience includes all members

* update ui

* address comments

* improve copy

* address zero-member edge case
2026-06-20 14:24:04 -07:00
Vikhyath Mondreti 13b5d215e9 improvement(access-controls): docs, terminology, fix delete bug (#5141)
* improvement(access-controls): dedup independent block names

* improvement(access-controls): fix delete button, naming in perm group modal
2026-06-19 13:48:07 -07:00
Vikhyath Mondreti 91f9dfdaec improvement(governance): derived access (#5134)
* improvement(governance): org-ws-credential roles clarity

* revert isHosted

* improvement(credentials): code cleanup

* address comments

* make kb cascade delete on user hard delete

* revert env flags

* chore(db): drop local 0242 migration to regenerate after merging staging

Our 0242 collides with staging's 0242. Remove it (and its snapshot +
journal entry) so the KB-cascade migration can be regenerated with the
correct number on top of the merged staging migrations.

* chore(db): regenerate kb→workspace cascade migration as 0243

Regenerated via drizzle-kit generate on top of the merged staging
migrations (staging took 0242). Re-applied the safety edits: NOT VALID
+ separate VALIDATE on the FK re-add, and the -- migration-safe note on
the DROP. check:migrations passes.

* improve copy

* update docs
2026-06-19 12:47:09 -07:00
Vikhyath Mondreti 58312a10e5 improvement(misc): add more sportmonks tools, improvestreaming ux (#5129) 2026-06-18 12:31:54 -07:00
Siddharth GanesanandVikhyath Mondreti e5f3965ed1 feat(mship): add parallel subagents, improve streaming performance (#5122)
* feat(subagents): add support for parallel subagents

* fix(subagents): address parallel-subagent bugs

* progress on streaming refactor

* improvement(subagents): update comment to reflect new go feature flag

* debug mode progress

* remove debug logs

* fix(validation): add escape annotation

* improvement(code): remove dead fallbacks

* fix subagent lane fallback issue

* fix(mothership): increase default redis event limit to 100k from 5k

* fix(mothership): streaming invariant projection enforcement

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-06-18 11:27:49 -07:00
Waleed 7d46103d09 chore(deps): remove unused dependencies and harden CI supply chain (#5119)
* chore(deps): remove unused dependencies and harden CI supply chain

Dependency cleanup:
- Remove unused deps: papaparse, unified, and 6 unused Radix primitives
  (alert-dialog, radio-group, scroll-area, separator, toggle, visually-hidden)
  plus @tanstack/react-query-devtools (all verified zero imports repo-wide)
- Consolidate jwt-decode into the existing jose dependency (decodeJwt)
- Migrate react-window to @tanstack/react-virtual to drop a redundant
  virtualization library (terminal, structured-output, code viewer)
- Remove the better-auth-harmony plugin and its gating env flag

Supply-chain hardening:
- SHA-pin every GitHub Action to a full commit SHA with a version comment
- Pin CI bun-version to 1.3.13 (was "latest" in the release job)
- Raise bun minimumReleaseAge cooldown from 3 to 7 days
- Add a non-blocking `bun audit` step in CI
- Add a CODEOWNERS gate routing dependency-manifest changes to @simstudioai/deps

* chore(deps): remove unused apps/docs dependencies (@tabler/icons-react, dotenv-cli)

* style(search-modal): use Send icon for Invite teammates action

* feat(search-modal): surface New chat as the top action above Create workflow

* feat(search-modal): add Secrets to the pages list
2026-06-17 14:56:43 -07:00
Waleed 11e23131fe feat(google): Maps Pollen/Solar, Custom Search expansion, and live-API fixes across Google integrations (#5113)
* feat(google): add Maps Pollen/Solar, expand Custom Search, fix Ads/Groups/Contacts/Slides

New capability:
- Google Maps: add Pollen Forecast and Solar Potential tools (API-key, google_cloud BYOK)
- Google Custom Search: add start/dateRestrict/fileType/safe/searchType/siteSearch/
  siteSearchFilter/lr/gl/sort params, htmlTitle/htmlSnippet/formattedUrl/mime/fileFormat/
  cacheId/image result fields, and nextPageStartIndex pagination

Fixes (validated against live API docs):
- Google Ads: bump all tools from sunset v19 to v24
- Google Groups: forward OAuth credential under oauthCredential (was dropping token in 11
  ops), forward all update_settings fields, JSON.stringify update_settings/add_alias bodies
- Google Contacts: include required metadata.sources[].etag in updateContact body (fixed 400)
- Google Slides: remove unsupported GIF thumbnail mimeType (API only allows PNG)
- Google Sheets: wire delete_rows/delete_sheet/delete_spreadsheet into the V2 block
- Google Custom Search: throw on API error responses instead of returning empty success;
  num optional + Number-coerced; pagemap typed unknown

* docs(google): regenerate integration docs for new and updated operations

* fix(google_maps): correct Solar requiredQuality enum to BASE

The Solar API ImageryQuality enum is HIGH/MEDIUM/BASE (+ UNSPECIFIED) per the
live docs; there is no LOW. Selecting "Low" sent requiredQuality=LOW which the
API rejects as INVALID_ARGUMENT, and the valid BASE tier was unreachable.
Replace LOW with BASE in the tool param/output descriptions, the type union,
and the block dropdown.

* fix(google_maps): guard !response.ok in Pollen/Solar; use ?? for color channels

Address Greptile review:
- Pollen and Solar transformResponse now check !response.ok || data.error
  (matches the Custom Search fix); a gateway error without an error key in the
  body no longer returns empty/zeroed output silently.
- Pollen color channels use ?? instead of || so a legitimate 0 isn't treated
  as missing (consistent with the other numeric fields in the file).

* fix(google_maps): guard against NaN days in Pollen forecast

Address Cursor Bugbot: a non-numeric `days` input parsed to NaN and was
forwarded as `days=NaN` (the tool's `?? 1` only catches undefined, not NaN),
breaking the forecast call. The block now coerces invalid input to undefined,
and the tool defaults to 1 unless `days` is a finite number.

* fix(google): clamp Pollen days to 1-5; stop forwarding stale group settings fields

Address Cursor Bugbot:
- Pollen: clamp days to the documented 1-5 range (truncating fractionals) so 0,
  negatives, or >5 can't be sent to the API.
- Google Groups update_settings: the block has no dedicated settings subblocks,
  so forwarding name/description from params could leak stale values from
  create_group/update_group and unintentionally rename the group. Forward only
  oauthCredential + groupEmail from the block (the tool's own param schema still
  exposes the settings fields for the agent path).

* fix(google_sheets): fail fast on non-numeric delete indices

Address Cursor Bugbot: delete_sheet/delete_rows parsed deleteSheetId/startIndex/
endIndex with Number.parseInt but didn't validate, so non-numeric UI input became
NaN and was forwarded (the v2 delete tools only reject null/undefined), breaking
the batchUpdate. The block now throws a clear error when any of these is not a
valid number.

* fix(google_search): clamp num to 1-10 and normalize start

Address Cursor Bugbot: num was coerced with Number() but not bounded, so values
like 11 or fractionals reached the API and failed. The tool now truncates and
clamps num to the documented 1-10 range and only sends a positive integer start,
ignoring non-numeric/out-of-range input.
2026-06-17 12:15:48 -07:00
Waleed 8b93e43037 improvement(integrations): validate BigQuery/Forms/PageSpeed + regenerate integration docs (#5109)
* improvement(integrations): validate BigQuery/Forms/PageSpeed + regenerate integration docs

- BigQuery: mark null-defaulted outputs optional (get_table type/numRows/numBytes/creationTime/lastModifiedTime/location, list_datasets location, list_tables type, query totalBytesProcessed)
- Google Forms: add response pagination (pageToken + filter params, nextPageToken output), fix pageSize visibility, advanced-mode pagination subBlocks + filter wandConfig
- PageSpeed: add a 7th BlockMeta template (competitor benchmark)
- Regenerate integration docs; add manual intro sections to new datagma/dropcontact/enrow/icypeas/leadmagic pages

* fix(docs-gen): preserve apostrophes in tool descriptions when generating docs

The doc generator extracted tool descriptions with a character class that
excluded both quote types (['"]([^'"]...)['"]), so a double-quoted description
containing an apostrophe (e.g. "Find someone's email") was truncated at the
apostrophe — the generated docs/catalog showed stubs like "Find someone".

Anchor extraction on the actual opening quote (single/double/backtick), matching
the existing extractDescription helper, in both buildToolDescriptionMap and
extractToolInfo. Regenerated docs restore full descriptions across all affected
integrations (Apollo, Ahrefs, LeadMagic, Findymail, OpenAI, Slack, etc.).

* fix(docs-gen): resolve tools defined in a sibling file + scope params per tool

The doc generator located a tool's definition only by filename convention
(decompress.ts / index.ts), so file_decompress — which lives in compress.ts
alongside file_compress — fell back to index.ts and rendered an empty Input
table. It also read the params block from the first tool in a multi-tool file,
so every tool in such a file inherited the first tool's inputs/outputs.

- getToolInfo: when no candidate file declares the exact tool ID, scan the whole
  tool-prefix directory for the file that does.
- extractToolInfo: read the params block scoped to the specific tool, falling
  back to the full file for tools that inherit params via spread.

Regenerated docs eliminate ~50 empty/incorrect input tables across integrations
(clickhouse, rb2b, reddit, file, etc.); param-less OAuth-only tools correctly
keep an empty input table.
2026-06-16 23:05:30 -07:00
Vikhyath Mondreti 7b4626e547 improvement(perm-groups): allow workspace filter for permission groups (#5070)
* improvement(perm-groups): allow workspace filter for permission groups

* show errors correctly

* address comments

* address concurrent edit concern

* address locks

* address comments"

* index migration safety

* address at route level
2026-06-15 20:52:01 -07:00
Waleed b7d30c89f9 feat(google-calendar): wire freebusy, align tools with API v3, add calendar + sharing tools (#5084)
* feat(google-calendar): wire freebusy, align tools with API v3, add calendar + sharing tools

* fix(google-calendar): address review — trust offset timezones, make list_acl showDeleted usable, harden unshare error parse, clarify update attendees

* fix(google-calendar): wire list q/pageToken into block, harden invite PUT error parse

* fix(google-calendar): make list orderBy user-selectable, clarify update timeZone applies to start/end

* fix(google-calendar): require timeZone for recurring timed events, clarify recurrence replace semantics

* fix(google-calendar): validate grantee before building share ACL body

Throw a clear error when scopeType is user/group/domain but scopeValue is
missing or blank, instead of POSTing a scope-type-only body that the Calendar
ACL API rejects with an opaque error.
2026-06-15 20:32:58 -07:00
Waleed 05e8c7cd71 refactor(connectors): split client metadata from server runtime (#5076)
* refactor(connectors): split client metadata from server runtime + cover node:net in client bundle

The browser build broke with `Cannot find module 'node:net'`. Server-only
SSRF code in `input-validation.server.ts` (`dns/promises`, and since PR #5060
`undici` → `node:net`/`node:tls`) is statically reachable from the client
bundle via the tool/connector registries, which the workflow editor imports
for metadata. Node networking builtins have no browser shim, so Turbopack
cannot compile them for the client.

Two changes:

1. Split each connector's client-safe declarative metadata into a sibling
   `meta.ts` (`<name>ConnectorMeta`), mirroring the `XBlockMeta` /
   `BLOCK_META_REGISTRY` pattern. `connectors/registry.ts` is now the
   client-safe `CONNECTOR_META_REGISTRY` (+ `getConnectorMeta` /
   `getAllConnectorMeta`); the full registry with runtime fns moves to
   `connectors/registry.server.ts`. Client components consume the meta
   registry; the sync engine and knowledge API routes consume the server
   registry. This removes connectors from the client's server-only graph.
   Connector metadata is byte-for-byte identical before/after; runtime fns
   are untouched.

2. Extend the existing #4899 `turbopack.resolveAlias` browser stub — which
   already mapped `dns`/`dns/promises` to an empty module for the browser —
   to also cover `net`/`tls` (+ `node:` variants), since `undici` now pulls
   those in. The remaining tool/provider definitions still reach
   `input-validation.server` server-side; the browser-only stub keeps those
   Node builtins out of the client bundle while the real modules stay on the
   server, so SSRF validation and IP pinning are unaffected.

Connector authoring/validation skills updated to teach the meta.ts split.

* fix(icons): use Square logo glyph only, drop wordmark

* fix(connectors): share Discord max-messages default across meta and runtime

Discord defined DEFAULT_MAX_MESSAGES separately in meta.ts (config placeholder)
and discord.ts (sync behavior), which could drift. Export it from meta.ts and
import it in the runtime, matching the single-source pattern used by the other
connectors (e.g. gmail, intercom).

* refactor(tools): route grafana/agiloft egress server-side, drop SSRF browser shim

Move the server-only SSRF-pinned fetch out of the grafana (update_dashboard,
update_alert_rule) and agiloft (11 record/search tools) definitions and into
internal API routes, the same pattern the rest of the server-side tools (and
agiloft's own attach/retrieve) already use. The tool definitions are now purely
declarative (request → internal route), so they no longer import
`input-validation.server` and the tools registry is fully client-safe.

With connectors (meta split) and these tools no longer reaching server-only code
from the client bundle, the browser no longer pulls in `dns`/`net`/`tls`:

- Add `import 'server-only'` to `input-validation.server.ts` so any future client
  import fails loudly at build time instead of silently bloating the bundle.
- Remove the `turbopack.resolveAlias` browser stub and delete
  `empty-node-fallback.browser.ts` — the root cause is fixed, the shim is gone.

Behavior is unchanged: each route runs the exact merge/validation/fetch logic the
tool ran before (every header, param branch, JSON-parse guard, error string, and
SSRF pinning preserved); only the location of execution moved from the client-
bundled definition to a server route.

* fix(connectors): move onedrive tagDefinitions into meta; drop server-only guard

- onedrive's tagDefinitions lived in the runtime file, so the client meta
  registry returned undefined for it and the add-connector tag opt-out section
  stopped rendering for onedrive. Move it into meta.ts like the other connectors
  so client and server see identical metadata (verified across all 50).
- Remove the 'server-only' import from input-validation.server.ts: the meta/route
  split already keeps it out of the client bundle, and blocks/tools registries
  don't use the guard either.

* fix(grafana): surface upstream error when the prefetch GET fails

Check response.ok on the existing-resource GET in both update routes and return
the upstream status/body, matching how the tool framework surfaced GET errors
before the move to internal routes (the framework checks response.ok before
transformResponse). Without this, a failed prefetch produced a generic
'Failed to fetch existing ...' message and dropped Grafana's error detail.

* fix(grafana): reject invalid panels JSON instead of silently ignoring it

Grafana's dashboard API treats panels as a required array and returns 400 on
invalid JSON; this route already errors on every other JSON param. Return
'Invalid JSON for panels parameter' instead of swallowing the parse error and
proceeding with a misleading success.

* fix(grafana): trim dashboard/alert-rule UID in route URLs (carry over #5082)

PR #5082 added .trim() on dashboardUid/alertRuleUid in the original tool URL
builders. Those tools now build their URLs in the internal routes, so apply the
same trim there to preserve that behavior.

* fix(grafana): route update_folder egress server-side (carry over #5082)

#5082 added a grafana update_folder tool that does SSRF-pinned fetch in its
postProcess, re-introducing the client-bundle leak. Convert it to the internal
API route pattern like the other update tools so the def is declarative and
input-validation.server stays out of the client bundle.

* fix(grafana): surface route failures in transformResponse instead of masking them

The grafana update tools' transformResponse hardcoded success: true and dropped
the route's error, so an upstream/validation failure (HTTP 200 with
{ success: false, error }) was reported to the workflow as a success. Forward
data.success and data.error (matching the agiloft tools) so failures propagate
as before the move to internal routes.
2026-06-15 19:53:37 -07:00
Waleed 324189ed39 feat(grafana): validate integration and add folder, health, and contact-point tools (#5082)
* feat(grafana): validate integration and add folder, health, and contact-point tools

- Require alert-rule title/ruleGroup/data in the block (create would 400 without them)
- Trim UID path params across dashboard and alert-rule tools to avoid copy-paste 404s
- Use Grafana brand color for the block background
- Surface previously-unsettable list params (limit, starred, annotation type)
- Add get/update/delete folder, check data source health, get health, and create contact point tools
- Strip non-TSDoc comments; regenerate docs and integrations.json

* fix(grafana): scope block param remaps per operation to prevent cross-operation leaks
2026-06-15 19:09:16 -07:00