mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
41fb86355d7006c2e383f6727027eee2583785d2
3719
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
41fb86355d |
fix(webhooks): validate and pin EmailBison apiBaseUrl before outbound requests (#5415)
* fix(webhooks): validate and pin EmailBison apiBaseUrl before outbound requests Route Email Bison webhook create/delete requests through the shared DNS-validated, IP-pinned fetch used by the Teams and Slack webhook providers instead of a raw fetch to the user-configured instance URL. * fix(webhooks): restore NEXT_PUBLIC_APP_URL in emailbison tests, dedupe strict-delete warning log Test env var mutation was never restored, risking cross-file leakage in single-threaded vitest runs. Strict-mode deleteSubscription failures were logged twice (once at the throw site with context, once generically by the outer catch); the outer catch now skips its own log for errors already logged at the throw site. |
||
|
|
a1fbb5743d |
fix(stt): bound audio download response size (#5412)
* fix(stt): bound audio download response size Cap the audioUrl download in the STT proxy route at the platform's standard 100MB file-size ceiling, matching the pattern already used by sharepoint/download-file and other external download routes. Classify size-limit rejections as a clean 413 instead of an unhandled 500. * fix(stt): avoid double isPayloadSizeLimitError call in error handling |
||
|
|
1a371e51e9 |
fix(uploads): bound multipart body read in workspace-file and knowledge-document upload routes (#5413)
* fix(uploads): bound multipart body read in workspace-file and knowledge-document upload routes * fix(uploads): avoid FormData-body stream race in bounded-read tests * fix(uploads): share MAX_MULTIPART_OVERHEAD_BYTES constant across upload routes |
||
|
|
b98c7c4136 |
fix(tools): bound download response size for drive/slack/onedrive (#5414)
Google Drive, Slack, and OneDrive download routes fetched provider file content without a response size cap, unlike the SharePoint download route. Add maxResponseBytes to each content fetch, reject Google Drive files whose metadata size already exceeds the cap before starting the download, and map the resulting size-limit error to a clean 413 response. |
||
|
|
04432b64a8 |
fix(copilot): validate credential-link URL scheme before rendering (#5416)
* fix(copilot): validate credential-link URL scheme before rendering Only render the credential connect link as a clickable anchor when its value resolves to an http(s) URL, reusing the isSafeHttpUrl helper already used for chat file links. * refactor(copilot): move isSafeHttpUrl to shared lib/core/utils/urls Per Greptile's convention feedback: isSafeHttpUrl was consumed by both chat and workspace/home but defined inside a feature-specific 'use client' component. Move it alongside getBrowserOrigin (which it already depends on) in lib/core/utils/urls.ts, matching this repo's shared-utility rule. |
||
|
|
acf4afb1b5 | fix(mothership): block subflow re-parenting in embedded workflow view (#5418) | ||
|
|
82eff5435d |
feat(custom-block): deploy a workflow as a reusable org-scoped block (#5407)
* feat(custom-block): deploy a workflow as a reusable org-scoped block * fix(custom-block): reseed deploy form, guard duplicate publish, run child deployed * test(custom-block): isolate custom-block rows fetch in execution-core test * fix(custom-block): allow cross-workspace exec, org-scope authority, keep field ids, hide disabled * feat(custom-block): run child under source owner's identity, workspace, and env * fix(custom-block): bind publish authz to the source workflow's workspace * fix(custom-block): gate edit/delete on source-workspace admin, not org admin * chore(custom-block): rebaseline route count to 887 after staging merge * fix(custom-block): sanitize failure output so it can't leak source workflow internals * fix(custom-block): derive inputs and curated outputs from deployed state, not draft * fix(custom-block): hide disabled blocks from the toolbar palette too * fix(custom-block): bill nested + failed-run hosted cost; expose real inputs to the agent * fix(custom-block): enforce enterprise + flag gate at every consumption path |
||
|
|
818fa00133 |
fix(mothership): stop chat perf decay from permanently-animated streamed messages (#5411)
* fix(mothership): stop chat perf decay from permanently-animated streamed messages * fix(mothership): reset animation latches when a reused ChatContent gets replaced content * fix(mothership): keep streaming parser on settled messages to kill the drain-swap flash * fix(mothership): unify plain/special render branches to stop whole-message re-fade when options arrive * improvement(mothership): hold render-phase animation latches in useState per updated hook rules * fix(styling): translucent text-selection in form controls so field text stays readable * improvement(styling): one lighter translucent text-selection color everywhere, no forced text color * chore(styling): selection-muted tokens as 8-digit hex to match color token convention |
||
|
|
0249516cab |
fix(compare): swap LangChain logo, fix comparison-table horizontal scroll (#5409)
* fix(compare): swap LangChain logo, fix comparison-table horizontal scroll
Replace LangChainIcon's path with the official brand mark (light-blue
link icon) instead of the prior monochrome recreation.
The comparison table's grid cells were missing min-w-0, so a grid item
without it sizes to its content's max-content width instead of
respecting its column's fr track, pushing the whole table wider than
its container and forcing a horizontal scrollbar. Add min-w-0 to every
grid cell/header, and size the row-label column to minmax(140px,
max-content) instead of a guessed fr ratio, so it's exactly as wide as
its longest label ("Vetted first-party integrations") needs and no
wider, leaving the Sim/competitor value columns their full share.
* fix(compare): stacked mobile layout for the comparison table
Below sm (640px) a 3-column table has no room to be legible even with
the sticky label column, so switch to the standard responsive-table
pattern instead: each fact stacks as label -> Sim's value -> the
competitor's value, each value tagged with its product name since the
column headers are no longer directly above. Pure CSS (max-sm:/sm:
variants), no JS, keeping this a zero-hydration server component.
* fix(compare): fix SourceLink inline-anchor overflow, align table breakpoint
Root cause of the persistent table-overflow/mobile-scroll issues: SourceLink
renders a plain <a> with no explicit display, so it defaults to
'display: inline'. min-width and truncate are no-ops on inline elements, so
any fact with a source (nearly all of them) ignored its flex/grid parent's
width constraint and could force the whole row (and thus the whole table)
wider than intended, regardless of any container-level min-w-0/max-content
fix. Give the anchor 'block min-w-0' so width constraints and truncation
actually cascade down to the wrapped value.
Also aligns the table's mobile-stack breakpoint from an ad hoc sm (640px) to
lg (1024px), matching this route group's own tablet-and-below convention
(.claude/rules for the (landing) group), and fixes the stacked mobile cells
to override the cell's base items-center with items-stretch so the name tag
and value get a real full-width box to truncate within instead of shrinking
to their own content size with no boundary.
* fix(compare): add min-w-0 to ColumnHeader per Greptile review
ColumnHeader (the Sim/competitor logo header cells in the two fr columns)
still had default min-width: auto, so an unusually long competitor name
could size the header to its min-content width and push the grid wider
than its column allows, same root cause as the rows already fixed.
|
||
|
|
759dddbf4c |
feat(billing): dedicated Credit usage page with date-range filter and CSV export (#5405)
* fix(billing): apportion per-row credit costs so they sum to the page total Cursor Bugbot (medium): each row rounded its own dollar cost to credits independently while the header total rounded the summed dollars once — over enough rows those two roundings can visibly disagree, the exact "line items don't add up to the total" class of bug apportionCredits was already built to prevent (used by the trace view / cost breakdown). Route now apportions each page's row credits against that page's dollar sum instead of rounding rows independently. Added a test with three sub-cent rows that would each independently round to 0 credits (but sum to 1) to prove the reconciliation holds. * fix(billing): dim stale credit usage rows while a new period loads Cursor Bugbot (medium): keepPreviousData kept the prior period's rows and total on screen while a newly selected period fetched, but the dropdown label updated immediately — so during the transition the displayed numbers were labeled under a period they didn't belong to. Now reads isPlaceholderData (the standard TanStack Query signal for "this data is a stale placeholder, not a fresh fetch for the current key") and dims the list while it's true, matching the same flag already used for this exact purpose in integration-skills-section.tsx. * fix(billing): show "<1 credit" for rows apportioned to 0 Cursor Bugbot (low): with apportioned per-row credits, a row with a real but sub-credit dollarCost can legitimately apportion to 0 credits once a sibling row absorbs the shared rounding remainder — rendering a flat "0 credits" reads as if nothing was charged, inconsistent with formatCreditCost's "<1 credit" wording used elsewhere in billing. Added dollarCost to the wire response (needed to distinguish a genuinely free row from a rounded-to-zero one) and a small formatRowCredits helper that only changes the label, not the underlying creditCost number, so the page-total reconciliation from the prior fix is unaffected. * fix(audit-logs): fix broken Custom range picker, trim time-range presets Custom range silently did nothing: the time-range trigger was a ChipSelect (Radix DropdownMenu, modal by default), and selecting "Custom range" opened the Calendar popover in the same tick the modal menu began its close/focus-lock cleanup, trapping the popover non-interactive. Swapped to ChipCombobox (Radix Popover, non-modal), mirroring the already-working pattern in the main Logs page exactly. Also trimmed the preset list from 11 to 8 entries (dropped Past 30 minutes/12 hours/14 days) so the menu fits without scrolling. * feat(billing): dedicated Credit usage page with date-range filter and CSV export Follow-up to #5391 per team feedback in Slack: move the credit usage list out of the inline Billing section into its own page, redesign rows to show source ("Chat", "Workflow: <name>") instead of a raw model description + badge, and add real date-range filtering and export. - Billing settings now shows a compact glance (30-day total + a "View usage logs" link) instead of the full inline list. - New /settings/billing/credit-usage page (sibling of [section], mirrors the secrets/[credentialId] detail-route pattern) with day presets (Today/7d/30d/All time) plus a working Custom range picker — the same ChipCombobox+Popover+Calendar wiring the audit-logs fix in this branch uses, not the broken ChipSelect pattern. - Rows show the humanized source label, or "Workflow: <name>" for workflow-sourced events (new server-side workflow-name lookup, batched per page). Dropped the redundant badge and raw model description. - CSV export of the currently-filtered logs via a new GET .../usage-logs/export route (mode: 'text' contract, synchronous single-response CSV — the dataset is a bounded per-user ledger, not a workspace-wide export, so no async job queue needed). Query-filter logic (date-range resolution, workflow-name lookup) is shared with the list route via shared.ts rather than duplicated. - period/startDate/endDate live in the URL via a co-located search-params.ts; the list query keeps keepPreviousData + isPlaceholderData dimming during filter transitions, matching the behavior already shipped in #5391. Verified live end-to-end: back link navigation, custom range picker opens and applies, day presets, CSV export downloads and matches the on-screen rows exactly (credits reconcile with the total), compact Billing summary + link. * refactor(billing): move workflow-name enrichment into getUserUsageLogs, dedup helpers /simplify pass over the credit-usage-page branch (4 parallel review angles: reuse, simplification, efficiency, altitude): - getUserUsageLogs now LEFT JOINs workflow and returns workflowName directly (matching lib/logs/list-logs.ts's established pattern), eliminating the route-layer resolveWorkflowNames query that both the list and export routes previously ran independently. - Added includeSummary (default true) to getUserUsageLogs so the export route's cursor loop can skip the cursor-independent SUM/GROUP BY aggregate it never reads — that aggregate was being recomputed on every page of a paginated export for no reason. - Fixed an off-by-one in the export's pagination loop: `<= MAX_EXPORT_ROWS` let it fetch one more full page past the cap only to discard it; `< MAX_EXPORT_ROWS` with a shrinking per-page limit never overshoots. - Deduplicated the SOURCE_LABELS map (was defined identically in both the page and the export route) into a shared, DB-free source-labels.ts both can import. - Export route now builds CSV rows via lib/table/export-format.ts's toCsvRow/formatCsvValue instead of a hand-rolled escaper. - Added formatApportionedCreditCost to conversion.ts so the page's row rendering shares its zero/sub-credit wording with formatCreditCost instead of re-deriving the same three-way branch. - Replaced the generic requireStartDateForCustomPeriod<Schema> contract helper (nontrivial generic bound for a single four-line refine used at two call sites) with a plain shared error-options object. - Removed the credit-usage page's dateRangeAppliedRef guard — a controlled Radix Popover never re-invokes onOpenChange in response to the parent's own setState call, so the guard was defending against a re-entrant close that can't happen. - Added a modal prop to ChipSelect (forwarded to the underlying DropdownMenu, which already supported it) so a future call site that hits the same "modal select traps a same-tick Popover" bug the audit-logs Custom range fix worked around has a real fix available instead of having to swap components again. Re-verified live end-to-end after the refactor: workflow-name resolution, credit reconciliation, and CSV export all still correct. * fix(billing): drop Dollar cost from the CSV export, strip inline comments We only surface credits to the user, not the underlying dollar figure — "Dollar cost" was the one place the export literally displayed a dollar amount (the rest of the codebase uses dollarCost purely as an internal signal to distinguish a sub-credit charge from a genuinely free event, never rendered as a "$" value). * fix(billing): export honors partial custom date range, surfaces truncation Greptile (P1) and Cursor Bugbot independently caught the same bug: handleExport only forwarded startDate/endDate when BOTH were truthy, but the list query and both API contracts treat endDate as optional for a custom period (defaults to now). A user landing on a bookmarked ?period=custom&startDate=... URL would see populated rows and an enabled Export button, then get a 400 on click since the export omitted the required startDate too. Fixed by forwarding each date independently, matching the list query's existing behavior. Also addressed Greptile's other two findings: - The export route now sets X-Export-Truncated so a 5,000-row-capped download is visible to the user (a toast), not just a server log. Reading that header meant switching the trigger from a plain anchor navigation to fetch+blob — an anchor can't inspect the response before the browser commits to the download. - resolveDateRange now throws explicitly when a custom period is missing startDate instead of silencing the null check with `as string`, which would have produced a silent Invalid Date if ever called without prior contract validation. * fix(billing): remove the export's arbitrary row cap, fix a cursor pagination bug it exposed A personal credit ledger doesn't have the same unbounded-growth problem a workspace table does — capping the export at 5,000 rows just meant long-tenured or high-usage accounts (exactly the ones most likely to need a full export to reconcile a billing question) got silently truncated. Replaced the cap with a 50,000-row circuit breaker that should never fire in normal use (logged as an error, not a warning, if it ever does) and bumped the page size from 500 to 1,000 to cut round trips. Removing the cap surfaced a real, pre-existing bug in getUserUsageLogs's cursor pagination: a raw `sql` template embedded a JS Date object directly as a bound parameter, which the postgres driver can't serialize (unlike drizzle's typed gte/lte operators, which already handle Date correctly elsewhere in the same function). It only ever manifested past the first page, which nothing before this export route's tight multi-page loop reliably exercised. Replaced the raw sql template with drizzle's typed lt/eq/or/and operators, matching the pattern already proven correct in this file. Verified live: seeded 6,000 rows (past the old cap) and confirmed the export downloads all of them in one request with credits reconciling exactly against the total. * perf(billing): skip the redundant cursor lookup when the caller already has it The export loop holds the previous page's rows in memory, so its next cursor's createdAt is already known — getUserUsageLogs was still re-resolving it via an extra DB round trip every page regardless. Added an optional cursorCreatedAt to skip that lookup when provided; the list route's existing callers are unaffected since they don't pass it. Verified live: zero cursor-lookup queries fired across a 3,500-row / 4-page export that previously issued one per page. * fix(billing): apportion credits over the whole filtered set, not per page/call Cursor Bugbot caught this: the list route apportioned each page's rows against only that page's own dollar total, while the export apportioned every exported row against the complete set's total. Since apportionment depends on the full set, the same log could show a different creditCost between the list and the export, or even between two pages of the same "Load more" list — and the sum of every loaded row could visibly drift from the "Total" header shown above them once more than one page had loaded. Extracted getUsageCreditsByLogId — a single, shared, whole-filter apportionment lookup both routes now call instead of each computing their own subset locally. The list route calls it once per page request (same cost profile as the summary aggregate it already pays for every page); the export calls it once before its pagination loop, not per page, keeping the round-trip count this session's earlier fix already reduced. Also extracted the condition-building shared by the main query, the summary aggregate, and this new lookup into one buildUsageLogConditions helper, removing a third copy of that logic. Verified live: summed every row across 4 "Load more" pages and confirmed it now matches the reported total exactly (previously could drift), and confirmed the list and the export produce byte-identical credit sequences for the same rows. * fix(billing): make custom-range startDate/endDate nullable, not '' defaulted startDate/endDate had no sensible static default (they're only ever meaningful mid-custom-range), so defaulting them to '' via .withDefault('') meant switching back to a preset left the URL carrying startDate=&endDate= instead of dropping the params entirely. Made them nullable (no .withDefault) instead, matching the identical fields in the main Logs page's own search-params.ts. Verified live — switching from a custom range back to a preset now clears both params from the URL completely. * feat(audit-logs): add CSV export, matching the Credit usage page pattern Adds an Export chip to the top-right of the Audit Logs page (via SettingsPanel's actions slot — the same header mechanism the Credit usage page uses), downloading every audit log matching the current search/type/date filters as CSV. - New GET /api/audit-logs/export route: same session + enterprise admin/owner gating as the existing list route, reuses the shared buildFilterConditions/buildOrgScopeCondition/queryAuditLogs helpers (already using drizzle's typed operators for cursor pagination, not the raw-sql-with-embedded-Date pattern fixed elsewhere this session), and the same fetch+blob+X-Export-Truncated pattern the Credit usage export already established. - Capped at 10,000 rows (not the 50,000 used for a personal credit ledger) — an org's audit trail can genuinely grow much larger than one user's usage history, so this is sized for "a reasonable audit review window," with truncation surfaced via a toast rather than silently dropped. - Bumped the API-validation-contract audit's route-count baseline for the new route. Verified live against a real enterprise org: switched to "All time," exported ~750 real audit log rows, confirmed formatting (quoted descriptions, actor email fallback) and correct filter scoping. * fix(billing): skip wasted credit apportionment on the summary fetch, block export during stale data Cursor Bugbot caught two real issues: 1. The compact Billing summary glance (limit=1) only ever reads summary.totalCredits, but the list route unconditionally ran getUsageCreditsByLogId's whole-filter scan on every call including this one — pure wasted work for a caller that discards the result. Added an includeCredits query flag (default true, using the shared booleanQueryFlagSchema) so useUsageSummary can opt out; the main paginated view keeps it on since it genuinely needs per-row values. 2. Export stayed enabled while useUsageLogs held stale rows via keepPreviousData mid-filter-transition — a user could change the period/range and click Export before the new data loaded, exporting against the new filter while the table still showed the old one. Export is now also disabled while isPlaceholderData is true. * fix(billing): deterministic apportionment order, block audit export during stale data Cursor Bugbot caught two more real issues on the latest push: 1. Same stale-export bug as the earlier Credit usage fix, this time in Audit Logs: Export stayed enabled while useAuditLogs held prior rows via keepPreviousData, so it could export against a just-changed filter while the table still showed the old one. Now also disabled while isPlaceholderData is true. 2. getUsageCreditsByLogId had no ORDER BY before apportionCredits's largest-remainder tie-break, so which row absorbed a tied remainder credit depended on undefined Postgres row order — the same event's displayed credit could flip between calls (list vs. export, or even two successive requests). Added the same `orderBy(desc(createdAt), desc(id))` the main list query already uses, making the tie-break reproducible. Verified live: 3 identically-costed rows produced the same tie-break winner across 3 repeated requests (previously order-dependent). * fix(billing): distinguish a failed summary fetch from zero usage The compact Billing glance only branched on isPending, so once useUsageSummary settled into an error state, totalCredits stayed undefined and formatCreditsLabel(0) rendered "0 credits" — visually identical to genuinely having no usage this period. Now shows the same neutral "—" placeholder for isError as it already does for isPending. * fix(billing): gate the credit-usage page server-side for enterprise accounts Greptile (P1) caught this: hiding the "View usage logs" link on the Billing page for enterprise accounts doesn't stop direct navigation — anyone with the URL (bookmark, shared link, browser history) could still reach the full page and its CSV export, which enterprise accounts were never supposed to see at all (billing is managed out-of-band for them). Added a server-side check in page.tsx before anything renders: resolve the session, look up the highest-priority subscription, and redirect to /settings/billing if it's enterprise — matching how getHighestPrioritySubscription is already used elsewhere for server-side plan checks, rather than relying on a client-side-only conditional the way the Billing page's inline section does. Also fixes loading.tsx: it was a Server Component (no directive) passing a raw icon function reference into the client Chip component, which fails RSC serialization. Added 'use client'. Verified live in a real browser against both an enterprise account (redirects to Billing before any credit-usage content renders) and a non-enterprise account (reaches the page normally). |
||
|
|
d5082013a2 |
fix(ui): surface silent validation and rejection errors across editors and modals (#5394)
* fix(ui): surface silent validation and rejection errors across editors and modals * improvement(knowledge): toast chunk validation errors instead of inline footer text * fix(tables): reject invalid date/number in expanded cell editor, dedupe invalid-input toasts * fix(knowledge): toast every failed chunk validation attempt, replacing the previous toast * fix(knowledge): dismiss stale validation toast once content validates * revert(ui): drop chat identifier error rendering and profile-name toast |
||
|
|
88330b43bf |
fix(uploads): gate execution-context uploads behind write/admin permission (#5404)
Fallback multipart upload route (/api/files/upload) had no workspace permission check for execution-context uploads, unlike the primary presigned-upload route which requires write/admin. Mirror that gate so both paths enforce the same access control. |
||
|
|
afe3d32032 |
fix(mcp): pass SSRF-guarded fetch into OAuth callback token exchange (#5399)
Mirrors the same wiring already used by probe.ts and revoke.ts, so the callback's token-exchange request goes through the same guarded fetch as the rest of the OAuth flow. |
||
|
|
1574c20806 |
fix(mcp): pass SSRF-guarded fetch into OAuth start flow, matching probe/revoke (#5398)
Discovery and registration during the MCP OAuth start flow were using the default global fetch. probe.ts and revoke.ts already route these calls through createSsrfGuardedMcpFetch(); this brings the start route in line with the same pattern. |
||
|
|
ff8844c7aa |
fix(guardrails): authorize vertexCredential before use in hallucination validation (#5400)
* fix(guardrails): authorize vertexCredential before use in hallucination validation The guardrails validate route now checks credential access via authorizeCredentialUse before passing a caller-supplied vertexCredential into hallucination validation, matching the existing pattern already used in the providers route for the same field. * fix(guardrails): gate vertexCredential authorization on the resolved provider Only run the credential check when the model actually resolves to vertex, matching the providers route's gating exactly, and drop a redundant fallback now that workflowId is already guaranteed present at that point. |
||
|
|
6bc70cb149 |
fix(tables): verify workflow belongs to table's workspace before binding (#5397)
* fix(tables): verify workflow belongs to table's workspace before binding Add a check that a table workflow group's workflowId resolves to an active workflow in the table's own workspace, in both the create and update handlers, before it is persisted. * fix(tables): reorder JSDoc for mapWorkflowGroupError Greptile flagged the JSDoc for mapWorkflowGroupError as orphaned after validateWorkflowInWorkspace was inserted between the comment and the function it documented. Move the comment back above its function. |
||
|
|
fd98218e58 |
fix(gmail): strip CR/LF from header values before MIME assembly (#5395)
* fix(gmail): strip CR/LF from header values before MIME assembly Adds sanitizeHeaderValue and applies it to to/cc/bcc/subject/ inReplyTo/references and the attachment filename in buildSimpleEmailMessage and buildMimeMessage before they're placed into MIME header lines. * fix(gmail): sanitize attachment mimeType in Content-Type header attachment.mimeType was written verbatim into the Content-Type header, unlike the other header fields this PR sanitizes. Route it through the same sanitizeHeaderValue helper for consistency. |
||
|
|
2e3574407b |
fix(chat): fail-safe to noindex if the deployment lookup errors (#5396)
generateMetadata queried the DB with no error handling, unlike the identical query in api/chat/[identifier]/route.ts (which already wraps it in try/catch). There's no error.tsx under app/(interfaces)/chat/ — metadata resolution errors aren't caught by route-segment error boundaries at all, only the root global-error.tsx — so a DB hiccup during this lookup would take the whole page down to a generic error page instead of just failing to determine indexability. Catches the error, logs it, and defaults to noindex: if we can't confirm the deployment is safely public, that's the correct SEO default anyway, not a reason to crash the request. Flagged by Cursor Bugbot on #5388 (already merged); this ships the fix as a standalone follow-up since the underlying code is already live. |
||
|
|
ca2a52ccf7 |
feat(billing): expose credit usage log in Billing settings (#5391)
* feat(billing): expose credit usage log in Billing settings
Add a "Credit usage" section under Billing, below Invoices, showing a
paginated, period-filterable list of individual credit-consuming
events (model, tool, and fixed charges) for every plan except
Enterprise. Wires the existing (previously unused) usage-logs backend
to a proper contract, React Query hook, and emcn-styled UI:
- getUsageLogsContract in contracts/user.ts, broadened the source enum
to match the real usage_log schema
- Rewrote the route to use parseRequest per the API boundary rules
- useUsageLogs infinite-query hook, keyset-paginated
- CreditUsageSection: period dropdown, total-credits summary, row
list with source badges, "Load more"
- Extracted formatCreditsLabel in conversion.ts as the shared
formatter for already-converted integer credits
* fix(billing): move usage-log key factory out of the 'use client' boundary
Greptile P2: usageLogKeys lived in the 'use client' usage-logs.ts hook
file — importing it from a server component (e.g. a future prefetch)
would resolve to a client-reference stub and crash at build/SSR, the
same class of bug that hit tables' key factory before. Extracted to
hooks/queries/utils/usage-log-keys.ts, matching table-keys.ts and
folder-keys.ts.
Also renamed a shadowed `source` map param in the route to `sourceKey`
for clarity.
* chore(billing): dedupe the usage-log period literal type
The '1d' | '7d' | '30d' | 'all' union was hand-typed in three places
across usage-logs.ts and credit-usage-section.tsx. Extracted
usageLogPeriodSchema in the contract and derived UsageLogPeriod from
it, so the hook, the component, and the key factory all share one
definition instead of risking drift.
* improvement(billing): move credit usage period filter into the URL
/cleanup pass (nuqs rule, react-query-best-practices, emcn review):
- period was a plain useState, but it's exactly the kind of shareable
list filter sim-url-state.md calls out for nuqs — migrated to
billingParsers/useQueryStates so the selection deep-links, survives
reload, and matches every sibling settings section (recently-deleted,
inbox, teammates). Derives its literal values from
usageLogPeriodSchema instead of a fourth copy of the same union.
- Removed an unjustified showSelectedCheck={false} on the period
ChipDropdown — the one other usage in the codebase is a one-shot
action menu with no persistent selection; this is a real filter and
should show the check like the default intends.
- Added placeholderData: keepPreviousData to useUsageLogs — period is
a variable query key, so without it switching periods flashed the
loading empty-state instead of smoothly transitioning.
Verified live: deep-link with ?period=7d pre-selects and loads
correctly, switching periods updates the URL, and the selection
survives a hard reload.
|
||
|
|
4fe372b583 |
improvement(seo): extract shared withFilteredNoindex helper (#5392)
The `{ ...base, ...(isFiltered && { robots: { index: false, follow: true } }) }`
faceted-navigation noindex pattern was duplicated verbatim across five
catalog pages (integrations, models, blog, careers, pricing). Extracted to
lib/landing/seo.ts alongside buildLandingMetadata, giving the pattern a
name and a single point of change. Pure refactor — verified byte-identical
rendered output (robots meta + canonical) on baseline and filtered variants
of all five pages before and after.
|
||
|
|
8937005076 |
improvement(compare): tighten comparison-page copy, cut self-referential hedging (#5390)
* improvement(compare): tighten comparison-page copy, cut self-referential hedging Across sim.ts and all 20 competitor profiles, remove phrasing that describes a claim's own provenance/reliability (e.g. 'marketing claim', 'marketing copy states', 'as of this check') in favor of stating the fact directly. Also trims overlong run-on sentences into shorter, more direct ones. No numbers, dates, comparisons, confidence levels, or sources changed. * fix(compare): avoid accidental boolean-icon misparse from copy tightening Three facts whose tightened wording started with a bare 'No ' now matched the Yes/No boolean-icon convention (parseFactValue), collapsing a nuanced 'Partial' or plain descriptive sentence into a bare X icon. Rephrase with 'Not'/'not' so these render as full text again, matching pre-edit behavior. |
||
|
|
03e0e075e1 |
improvement(styling): consistent branded text-selection color app-wide (#5389)
* improvement(styling): use a consistent branded text-selection color app-wide Previously no ::selection rule was defined anywhere, so text selection fell back to the browser/OS default, which dims when a window loses focus and made pages look inconsistent side by side. Adds one global rule using the existing --selection token. * fix(styling): resolve markdown-selection conflict, use --white token Remove the local link/strikethrough ::selection color overrides in rich-markdown-editor.css now that the global ::selection rule already forces uniform white text on every selection. Also swap the literal #ffffff for the existing --white design token. |
||
|
|
7ff4f17eb2 |
fix(seo): fix GSC indexing issues, remove unused academy/partners pages (#5388)
* fix(seo): fix GSC indexing issues, remove unused academy/partners pages - robots.ts: unblock /chat/ (page-level noindex now gates gated/inactive deployments instead), drop the now-vestigial blog-tag/link-preview carve-out - next.config.ts: add missing redirects for renamed integration slugs (sap-s-4hana, calcom), removed /partners, and removed /academy - fix missing canonical/noindex on filtered catalog pages (integrations, models, blog, careers, pricing) causing GSC "duplicate, Google chose different canonical" - standardize page titles to "Page | Sim, the AI Workspace" across the board - remove the academy marketing pages and partner program page (content consolidated into docs.sim.ai/academy); drop the unused academy_certificate table via migration and strip the sandbox-mode plumbing from the workflow editor that only academy ever used * fix(migrations): defer academy_certificate table drop to a follow-up PR CI's expand/contract migration safety check correctly flagged this: the academy_certificate DROP TABLE was bundled in the same PR as removing the code that reads/writes it (the certificates API route had no feature-flag guard of its own, so it was reachable independent of the marketing pages being disabled). Dropping the table in the same deploy risks breaking any pod still running the old code during a rolling deploy. Restores the table/enum in schema.ts and the test mock, and removes the 0254 migration. The table drop should ship in its own PR once this one's code removal is confirmed live. * fix(seo): drop dead revalidate exports on searchParams-driven pages Any Server Component in the route tree reading searchParams forces the whole route to fully dynamic per-request rendering, which overrides ISR — revalidate is a silent no-op once that happens. True on pricing/careers because generateMetadata now parses searchParams directly, and was already true on blog before this PR (its page body already read searchParams). Flagged by Greptile on pricing; same root cause applies to all three. |
||
|
|
e1b8200660 |
fix(sso): support skipping the OIDC UserInfo endpoint at registration (#5386)
* fix(sso): support skipping the OIDC UserInfo endpoint at registration * fix(sso): cap OIDC discovery fetch at 10s to avoid stalling registration * test(sso): default-mock discovery fetch so intent is explicit * fix(sso): prefer client_secret_post and surface discovery failure reasons * fix(sso): always resolve token auth method and skip SSRF-checking a discarded userInfoEndpoint |
||
|
|
331875b7fe |
fix(billing): stop showing "View all" when there are no more invoices (#5387)
* fix(billing): stop showing "View all" when there are no more invoices Show fewer invoices (10) and derive hasMore from the finalized-invoice count instead of Stripe's raw has_more, which counted drafts we filter out client-side and could report more invoices than actually exist. * chore(billing): move invoice pagination explanation into TSDoc Extract collectFinalizedInvoices with a TSDoc comment explaining the Stripe has_more/draft-filtering rationale, instead of an inline comment block. * fix(billing): don't hide View all when the page-cap is hit inconclusively Greptile P1: if MAX_STRIPE_PAGES is exhausted while the finalized count sits exactly at MAX_INVOICES and Stripe still has_more, hasMore was silently returned as false. collectFinalizedInvoices now also returns whether Stripe's cursor was still open at exit, and the route ORs that into hasMore so the safety cap can never suppress "View all". Also asserts starting_after cursor propagation across pages and adds a test for the safety-cap-hit case. |
||
|
|
0cc290eeb7 |
feat(comparison): add Microsoft Copilot Studio, OpenClaw, Dust, CrewAI, and LangChain (#5384)
* feat(comparison): add Microsoft Copilot Studio, OpenClaw, Dust, CrewAI, and LangChain
- 5 new "Sim vs Competitor" profiles (now 20 total), each with ~58
independently sourced facts, standout features, and limitations,
researched against each vendor's own docs/pricing/GitHub
- New brand icons: MicrosoftCopilotIcon, OpenClawIcon, DustIcon,
LangChainIcon, CrewAIIcon
- isWorkflowBuilder: false for OpenClaw, CrewAI, and LangChain since
they're a personal agent runtime and code-first frameworks rather
than visual workflow builders, so their FAQ asks a
category-clarifying question instead of a peer feature-gap one
- Independent tone audit (no over-praising competitors, no unflattering
Sim framing) and a fresh accuracy re-verification pass (50 highest-
stakes facts across all 5 profiles, all confirmed against live
sources) both came back clean
* fix(comparison): fix two more FAQ text-mangling bugs found during final audit
- lowercaseFirst only guarded against 2+ CONSECUTIVE leading capitals
(acronyms like "AI"/"SSO"), so CamelCase brand names with a single
leading capital (LangChain, OpenClaw, CrewAI) got their first letter
wrongly lowercased ("langChain provides..."). Now checks for 2+
uppercase letters anywhere in the leading word, which covers both
acronyms and CamelCase brand names.
- parseFactValue and summarizeFact's boolean-prefix stripping only
recognized "Yes:"/"No:" (colon), but "Yes, ..."/"No, ..." (comma) is
an equally common phrasing already used across ~15 existing facts
(stackai, pipedream, workato, zapier, etc.), so those facts kept
their leading comma when stitched into an FAQ answer (e.g. "StackAI:
, broad support..."). Both now accept either separator.
Found by systematically sweeping every "Sim vs X" FAQ answer across
all 20 competitor pages for garbled/mis-cased text, not just the 5
newly added ones.
* feat(comparison): add sub-workflow composition and loop-block facts
- Two new universal comparison facts across all 20 profiles:
subWorkflows (calling a saved workflow as a reusable step inside
another) and loopIteration (a dedicated sequential for-each/while
loop container, distinct from concurrent Parallel execution)
- Both are real Sim capabilities (Workflow block, Loop block) verified
directly against the codebase and docs.sim.ai
- Findings are genuinely mixed, not uniformly favorable: n8n, Zapier,
Make, Workato, Retool, Power Automate, Gumloop, Vellum, Stack AI,
Tines, Langflow, Flowise, Microsoft Copilot Studio, and LangChain all
have some form of sub-workflow calling; Zapier and Gumloop's loop
primitives run concurrently rather than sequentially (marked
"Partial", not "No"); Pipedream, OpenAI AgentKit, Claude Cowork,
CrewAI, Dust, and OpenClaw genuinely lack one or both
* feat(comparison): add third-party integration vetting fact
New universal comparison fact across all 20 profiles: thirdPartyVetting,
whether a platform's integrations/tools/skills come from a vetted
first-party catalog vs. an open marketplace where any third party can
publish executable code with lighter or no vendor security review.
Directly relevant given OpenClaw's ClawHub marketplace has documented
incidents (283 skills, ~7.1% of the registry, found leaking credentials;
24 accounts distributing 600+ malicious skills before scanning existed).
Findings are honest and mixed, not uniformly favorable: Gumloop, Retool,
and Tines are first-party-only like Sim (marked "Yes"); n8n, Zapier,
Make, Workato, and OpenAI AgentKit have partial vetting on an open or
semi-open ecosystem; Pipedream and OpenClaw are open marketplaces with
documented security incidents.
* fix(comparison): correct n8n supply-chain attack download count
Independently re-verified the cited Hacker News article: the primary
malicious package had 4,241 downloads listed (not "3,400 weekly" as
previously written, a number not actually supported by the source).
* fix(comparison): fix duplicated competitor name in first FAQ answer
Every competitor's oneLiner is already a complete "{Name} is ..."
sentence, so prepending "${name} is " before it was always redundant.
Before this session's lowercaseFirst fix, the duplication rendered in
mixed case ("Zapier is zapier is a cloud-based...") and was easy to
miss; the CamelCase-name fix made it fully literal and obvious
("CrewAI is CrewAI is..."), which is what Cursor Bugbot caught.
Fixed by using the oneLiner directly (via ensurePeriod) instead of
re-prepending the name. Verified fixed across all 20 pages via live
curl, not just the CrewAI case Cursor flagged.
* improvement(comparison): cite Sim's own docs alongside code for 4 facts
Added docs.sim.ai citations (Roles and Permissions, BYOK, Debugging
retrieval, Function block) as primary sources for rbac, byok,
kbChunkVisibility, and customCodeSteps, which previously cited only
GitHub source code with no user-facing documentation reference.
Verified all 4 doc URLs resolve (200).
|
||
|
|
de110e02eb |
improvement(tables): harden pagination for short pages and surface name-validation errors (#5351)
* improvement(tables): harden pagination for short pages and surface name-validation errors * fix(tables): align getNextPageParam tests with count-based termination * improvement(tables): lift the 10K-char per-string-cell limit |
||
|
|
4086750ddc |
feat(comparison): add Sim vs Competitor comparison pages (#5383)
* feat(comparison): add Sim vs Competitor comparison pages - New /comparison hub + /comparison/[provider] detail pages for Sim vs 16 competitors (n8n, Zapier, Make, Gumloop, Workato, Retool, Pipedream, OpenAI AgentKit, Tines, StackAI, Power Automate, Vellum, Claude Cowork, Langflow, Flowise), each with ~60 sourced, dated facts across platform, AI capabilities, integrations, pricing, security, observability, and support - Data layer at lib/compare/data (types, per-competitor profiles) is UI-free and independently auditable - BreadcrumbList, ItemList, and FAQPage JSON-LD per page; sitemap picks up all pages automatically via ALL_COMPETITORS - Two new fact categories (parallel execution, Agent2Agent protocol) researched and sourced against every competitor's own docs * improvement(comparison): neutralize tone across competitor and Sim fact copy - Remove promotional/superlative adjectives applied to competitors (Zapier "one of the largest catalogs", Workato "notably wide", Retool "seamless"/"trusted solution", OpenAI "cutting-edge", Flowise "deep"/"mature"/"most widely adopted", Make "robust") - Reword the few places Sim's own accurate limitations read as unflattering rather than neutral fact (dynamic tool use, model fallback, durability, async execution, support channels, tracing) without changing the underlying facts - Restore the "Yes:"/"No:" value prefix on two Sim facts where it was accidentally dropped during rewording, since FactValue depends on that prefix to render the check/X status icon * fix(comparison): address review findings from Greptile and Cursor Bugbot - Fix parseFactValue regex to require a word boundary after Yes/No, so values like "Not documented"/"Not publicly documented" no longer get misread as a boolean "No" and render as neutral text again (Cursor) - Reword the self-hosting FAQ question to drop the false presupposition that the competitor doesn't self-host, which was wrong for n8n, Langflow, and Flowise (Greptile) - Rename isLastRow -> isNotLastRow in comparison-table.tsx to match what the variable actually computes (Greptile) - Move critters to devDependencies; it's a next build -time-only CSS inliner, not needed at runtime (Greptile) - Sitemap lastModified for comparison pages now matches the max(Sim, competitor) verified-date logic each page's own JSON-LD dateModified already uses, so the two never disagree (Cursor) * fix(comparison): fix doubled Yes prefix and missing period in hub FAQ - Two hub FAQ answers prepended "Yes." to fact values that already start with "Yes:", producing "Yes. Yes: ..." in visible copy and FAQPage JSON-LD. Export and reuse ensurePeriod instead of a hardcoded prefix. - The integrations-count FAQ answer ran two sentences together with no period before "Combined with...". Fixed with the same ensurePeriod helper. * improvement(comparison): remove redundant Key differences at a glance section The 5 facts it previewed (self-hosting, environment promotion, human-in-the-loop, pricing model, data residency) are the exact same rows shown again immediately below in the full comparison table, so the section read as pure repetition for a human scrolling past it. * fix(comparison): strip Yes/No prefix before lowercasing in summarizeFact summarizeFact fed the raw fact value through lowercaseFirst even when it started with "Yes: "/"No: ", producing broken mid-sentence FAQ text like "n8n: yes: many providers via dedicated Chat Model nodes." Strip the boolean prefix first, matching Greptile's suggested fix. |
||
|
|
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)
|
||
|
|
4085a1ea07 |
fix(resume): fix click-blocking footer overlay + hardening on HITL resume page (#5381)
* fix(resume): fix click-blocking footer overlay + hardening on HITL resume page - SupportFooter rendered position:absolute with no space reserved for it in InterfacesShell/AuthShell/file-share auth gate, silently overlapping and eating clicks on whatever content ended up in its ~50px footprint (on the resume page, the Resume Execution button itself) - Applied the same fix to /invite and /f/[token], which shared the identical absolute-positioning pattern - handleResume no longer fails silently on validation errors or unexpected exceptions - getPauseContextDetail no longer duplicates a pause point's full response payload in the same API response - Oversized HITL display-data values are now truncated in the resume page preview instead of rendering unbounded * fix(resume): use consistent string for truncation-notice length The object-branch truncation notice sliced from the prettified JSON.stringify(value, null, 2) but reported the total against the compact JSON.stringify(value).length, which could show a nonsensical "5,000 of 4,800 characters shown" when the compact form is shorter than the prettified slice. Derive both from the same string. * fix(resume): show em dash for empty-string display data values renderStructuredValuePreview only treated null/undefined as empty; an empty string fell through to the plain-text branch and rendered as a bordered, padded, contentless pill that reads as a stray UI element (e.g. an unstyled toggle) in the Display Data table. |
||
|
|
577a7a2ef1 | fix(landing): inset careers row hover, drop trusted-by section, gate contact submit (#5380) | ||
|
|
b4b666bfbe |
improvement(forking): fork time ux (#5348)
* improvement(forking): fork time ux * add storage quota * address comments * merge latest staging * address comments |
||
|
|
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.
|
||
|
|
06dd81150a |
fix(google-forms): fail-closed auth, legacy formId key fallback, idempotency (#5377)
- verifyAuth now rejects with 401 when no token is configured instead of silently allowing unauthenticated requests through - formatInput falls back to the legacy formId providerConfig key (pre-#3141 rename to triggerFormId) so old deployments keep working - add extractIdempotencyId keyed on formId:responseId to dedupe retried Apps Script deliveries |
||
|
|
f6b802e7cb |
fix(sendgrid): fix active field coercion, add pagination, tighten output typing (#5368)
* fix(sendgrid): fix active field coercion, add pagination, tighten output typing - Fix active field for create_template_version being sent as the string "true"/"false" instead of the SendGrid-required int 0/1 - Add missing authMode: ApiKey on SendGridBlock - Add pageToken/nextPageToken pagination support to list_templates and list_all_lists (SendGrid page_token cursor, parsed from _metadata.next) - Fix nullable output fields to use ?? null / ?? [] with optional: true across get_contact, search_contacts, remove_contacts_from_list, create_template_version, add_contact, send_mail - Remove dead data.templates fallback in list_templates (API only ever returns result) - Remove unused UpdateContactParams/UpdateListParams/UpdateTemplateParams dead types; make CreateTemplateParams.generation optional to match actual tool behavior * fix(sendgrid): address Cursor Bugbot findings on pagination and active coercion - Gate listPageToken/templatePageToken remap on operation so a stale token from the other list operation can't override the intended one - Fix active coercion to also treat a real boolean true (from a dynamic <Block.output> reference) as active, not just the dropdown string 'true' * fix(sendgrid): coerce active to int at the tool layer too Per Greptile: the block-level active coercion only covered the UI path. A direct sendgrid_create_template_version tool invocation with a boolean active would still send a raw boolean to SendGrid. Coerce to 0/1 in the tool's own request body so both paths are correct. * fix(sendgrid): always send page_size on list_templates SendGrid's GET /v3/templates requires page_size on every request (no server-side default) — omitting it errors. Default to 20 to match our own documented default when the caller doesn't set one. * fix(sendgrid): explicit false/'false' check for active flag Per Cursor Bugbot: params.active ? 1 : 0 treated any truthy string (including "false") as active. Extracted a toActiveFlag helper that only treats real false or the string 'false' as inactive, everything else (including unset) defaults to active — matches the tool's documented default. * fix(sendgrid): handle numeric 0 in toActiveFlag Per Greptile: the block coerces active to a number (0/1) before calling the tool, but toActiveFlag only checked for false/'false', so the block's inactive selection (0) fell through to the "active" branch. Check against an explicit inactive-values set covering the boolean, string, and numeric forms. * fix(sendgrid): nest add_contact custom fields under custom_fields Pre-existing bug (predates this PR): custom fields were merged onto the contact object as top-level sibling keys via safeAssign/Object.assign, but SendGrid's PUT /v3/marketing/contacts requires them nested under a custom_fields object. SendGrid silently drops unrecognized top-level keys, so the documented customFields param never actually reached SendGrid. Caught during a final adversarial re-verification pass before merge. * fix(sendgrid): document consistent page_size requirement for list_templates pagination Per Cursor Bugbot: list_templates always defaults page_size to 20 when unset (required by SendGrid), so a follow-up pageToken-only call after a first call with a larger pageSize would silently shrink to 20 and desync page boundaries. This is inherent to a stateless tool call (SendGrid requires page_size on every request, and the tool has no way to remember the prior call's value), so clarify via param description and UI placeholder that callers must repeat the same pageSize across paginated calls. * chore(api-validation): bump stale route-count ratchet baseline 883->884 Unrelated to the SendGrid work in this branch. staging's own HEAD already has 884 compliant Zod-backed API routes (0 non-Zod), but this ratchet baseline was never bumped when that route landed, so any PR rebasing onto current staging fails check:api-validation:strict with "route count increased from 883 to 884". All routes remain fully Zod-backed; this is a mechanical counter update, not a policy change. * fix(sendgrid): dedupe active coercion between block and tool Per Cursor Bugbot: the block's pre-coercion only recognized the dropdown string 'true' or boolean true as active, so a dynamic reference producing numeric 1 or string '1' fell through to 0 and silently created an inactive template version. Exported the tool's toActiveFlag and reused it in the block instead of duplicating the inactive-value logic, so both layers can no longer drift out of sync. |
||
|
|
11c7a36f11 |
feat(hex): expand API coverage, fix ID trimming, add cursor pagination (#5372)
* feat(hex): expand API coverage, fix ID trimming, add cursor pagination - Add hex_update_collection, hex_create_group, hex_update_group, hex_delete_group, hex_deactivate_user tools + block wiring - Add missing run_project fields (viewId, notifications) and get_project_runs runTriggerFilter - Add after/before cursor pagination to list_projects, list_groups, list_data_connections, list_collections - Add .trim() on all interpolated ID path params to guard against copy-paste whitespace * fix(hex): guard JSON.parse on user-supplied array/object params Wrap JSON.parse calls for memberUserIds, addUserIds, removeUserIds, inputParams, and notifications in try/catch so malformed input throws a clear error instead of an opaque parse exception. * fix(hex): forward empty collection description on update update_collection's params() mapping only copied collectionDescription when truthy, so clearing it to an empty string in the UI never reached the API. Untouched fields resolve to null (not undefined), so use a loose null check to distinguish "cleared" from "never touched" without sending description: null on every unrelated update. * fix(hex): close remaining API coverage gaps found via raw OpenAPI spec Pulled Hex's actual OpenAPI spec (static.hex.site/openapi.json) as ground truth for a final verification pass: - list_users was missing after/before cursor pagination and the userIds filter, which the spec confirms it supports (an earlier doc-summary pass had incorrectly flagged this as unverifiable) - list_users response also exposes lastLoginDate per user, not previously surfaced - list_projects was missing includeComponents, includeTrashed, creatorEmail, ownerEmail, collectionId, categories, sortBy, and sortDirection filters that the spec confirms are real query params * fix(hex): trim remaining user-suppliable IDs for consistency Trim collectionId (list_projects filter), viewId (run_project body), and group member UUIDs (create_group/update_group) to match the .trim() convention already applied to path-segment IDs elsewhere. * fix(hex): validate parsed JSON is actually an array before iterating categories, memberUserIds, addUserIds, and removeUserIds could parse as valid JSON that isn't an array (e.g. an object), which would throw an opaque "not iterable"/"map is not a function" error deeper in the call. Validate Array.isArray after parsing and fail with a clear message instead. * fix(hex): validate array element types, send explicit ALL trigger filter - notifications now gets the same Array.isArray guard already applied to categories/memberUserIds/addUserIds/removeUserIds - member/category array elements are now validated as strings before .trim()/append, instead of crashing on non-string entries - runTriggerFilter "All" option now sends the documented ALL enum value explicitly instead of relying on omission |
||
|
|
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. |
||
|
|
a8f27094f9 |
improvement(gong): validate integration against API docs, fix pagination/wandConfig consistency (#5361)
* improvement(gong): tighten pagination optionality and add wandConfig to ID list fields - mark cursor output optional:true on aggregate_activity, interaction_stats, list_flows for consistency with list_calls - add wandConfig to comma-separated ID fields (callIds, primaryUserIds, userIds, scorecardIds, reviewedUserIds) matching repo convention for CSV inputs * fix(gong): request context data for get_extensive_calls contentSelector.context/contextTiming were never set on the /v2/calls/extensive request, so the documented context (CRM/external-system links) output was silently always empty even though the field is declared in the tool's outputs. * feat(gong): add data privacy erase and Engage flow prospect tools - gong_purge_email_address / gong_purge_phone_number: POST /v2/data-privacy/erase-data-for-*, the write-half pairing with the existing lookup_email/lookup_phone read tools - gong_assign_flow_prospects: POST /v2/flows/prospects/assign, enrolls CRM prospects into an Engage flow - gong_get_prospect_flows: POST /v2/flows/prospects, looks up which flows a prospect is in Field names verified against an OpenAPI-generator-produced client (cedricziel/gong-rs) whose serde rename attributes mirror Gong's published spec, since Gong's interactive Swagger docs require an authenticated session and can't be fetched directly. * fix(gong): require human-entered target for data-erase tools emailAddress/phoneNumber on the purge tools were user-or-llm, letting an agent autonomously pick the erasure target for an irreversible operation with no human confirmation. Match the user-only visibility already used for credentials. |
||
|
|
4988ba621c |
feat(trello): expand tool coverage and fix API gaps (#5357)
* feat(trello): expand tool coverage and fix API gaps - add delete card, remove label/member, update list, add/update checklist item, list members, and search tools - add filter support to list lists/cards, since/before paging to get actions, member assignment on card creation - promote move-to-list field out of advanced mode * fix(trello): address review feedback on checklist item tooling - idChecklist can be absent on Trello checkItem responses, so stop treating it as required - validate state/name is provided before building the update-checklist-item request URL - reject Update Checklist Item at the block level when neither State nor New Item Name is set - add missing idOrganization field to search's board output schema |
||
|
|
a08da86dff |
feat(wordpress): add category/tag CRUD tools, fix delete-status and dead-field bugs (#5360)
* feat(wordpress): add category/tag CRUD tools, fix delete-status and dead-field bugs
- Add wordpress_{get,update,delete}_category and _tag tools for full taxonomy parity
- Fix `deleted: data.deleted || true` always evaluating true across all 6 delete tools (posts/pages/media/comments/categories/tags)
- Remove dead `force` param from delete_media (endpoint always force-deletes; param had zero effect)
- Remove unwired `hideEmpty` block input
- Normalize search_content perPage/page visibility to user-or-llm for consistency
* fix(wordpress): use ?? instead of || for zero-valued numeric fields in delete_category/tag
count and parent can legitimately be 0 (empty term, top-level category); || was
dropping those values, same antipattern already fixed for `deleted` in this PR.
Flagged independently by Greptile and Cursor Bugbot.
* fix(wordpress): use ?? for zero-valued numeric fields across all delete tools
Same || antipattern already fixed for category/tag delete tools was still
present in delete_post/page/comment/media for id, author, featured_media,
menu_order, parent, post — all legitimately 0 in common cases (no featured
image, top-level page/comment). Found by a final independent validation pass.
* fix(wordpress): don't drop categoryParent=0 (root-level category) in block param mapping
Truthy check on params.categoryParent treated a resolved numeric 0 (root-level,
no parent) as unset. Flagged by Cursor Bugbot on create_category/update_category.
* fix(wordpress): don't clear category/tag description on update when field left blank
description used !== undefined (numeric-field convention) instead of a truthy
check (string-field convention used everywhere else in this codebase, e.g.
update_post/update_page excerpt), so an untouched empty description field
silently wiped existing text on every update. Flagged by Cursor Bugbot.
* fix(wordpress): fix search type/subtype mislabeling, complete I/O exposure gaps
- search_content.ts: type param was mislabeled with subtype's vocabulary
(post/page/attachment); real WP type enum is post/term/post-format. Rewired
the block's Content Type dropdown to map to subtype (which is what
post/page/attachment actually filter), not type.
- Widened subBlock conditions so params already read by tools.config.params
are actually reachable in the UI: commentPostId for list_comments,
categories/tags for list_posts, parent for list_pages.
- Added missing subBlocks for tool params with no UI path: comment
parent/authorName/authorEmail/authorUrl (create_comment), media description
(upload_media), author filter (list_posts).
- Extended the ?? / !== undefined fix (already applied to categoryParent) to
the same class of param across the block: featuredMedia, page parent,
menuOrder, and the new commentParent/listAuthor mappings.
- Fixed featuredMedia/parent truthy-check inconsistency in create_post,
update_post, create_page, update_page, create_category, create_comment
body builders to match the !== undefined convention used elsewhere.
Found by an independent final validation pass across 3 parallel agents.
* fix(wordpress): keep searchType subBlock id to preserve saved-workflow compat
The type/subtype fix should only change which API param the field feeds
(subtype, not type) — renaming the subBlock id to searchSubtype broke
already-saved workflows with a search content-type filter set, since the
block would stop reading the old searchType key. Reverted the id rename,
kept the underlying subtype mapping fix. Flagged by Cursor Bugbot.
* fix(wordpress): remove invalid Attachment search subtype, fix listAuthor input type
- Search Content's "Content Type" dropdown offered Attachment, which maps to
subtype=attachment. WP core's WP_REST_Post_Search_Handler explicitly
excludes attachment from valid subtypes (media isn't searchable via
/search) — selecting it guaranteed a 400 rest_invalid_param. Removed the
option; only Post/Page (the only valid subtypes) remain.
- listAuthor was declared type: 'string' in the inputs catalog despite being
Number()-coerced before use, inconsistent with every other ID-like field
(postId, pageId, categoryId, commentParent, etc. are all 'number').
Found by an independent final pre-merge validation pass, requested before
merge to be certain of full API alignment.
|
||
|
|
7a31871471 |
feat(clerk): expand Clerk integration with org, membership, moderation, and security tools (#5364)
* feat(clerk): expand Clerk integration with org, membership, moderation, and security tools - fix 4 validate-integration warnings: missing .trim() on org/session IDs, incomplete session-status dropdown, missing list_users/list_organizations filter subBlocks - add organization update/delete tools - add organization membership CRUD (list, add, update role, remove) - add organization invitation create/list - add user ban/unban/lock/unlock and OAuth access token retrieval - add allowlist/blocklist identifier management - add JWT template list/get - add actor token create/revoke (impersonation) - add matching webhook triggers for session ended/removed/revoked, organization updated/deleted, and membership updated/deleted - wire all 23 new tools into the block, tool registry, and trigger registry * fix(clerk): I/O completeness fixes from final validation pass - remove dead limit/offset params from list_blocklist_identifiers (Clerk API accepts zero params on this endpoint, verified across 6 spec versions) - expose publicMetadata on OAuth access token output (was silently dropped) - expose inviter email/first/last name (public_inviter_data) on organization invitation create/list outputs - add missing orderBy param to list_organization_invitations |
||
|
|
f33d325a88 |
fix(deps): bump echarts to 6.1.0 to patch XSS vulnerability (#5374)
Fixes GHSA-fgmj-fm8m-jvvx / CVE-2026-45249 — Lines series tooltip rendering could execute raw HTML from series.data[i].name when no custom tooltip.formatter is set. |
||
|
|
070f554047 |
feat(sharepoint): validate against Graph API and add delete/update/download tools (#5369)
* feat(sharepoint): validate against Graph API and add delete/update/download tools - Fix bugs found validating existing tools against live Graph docs: malformed nested $expand syntax in get_list, reversed site-resolution precedence in create_list, unsanitized field fallback in add_list_items, missing URL encoding in read_page, and an incorrect root/serverRelativeUrl field shape - Fix V1 block gaps vs V2: upload_file required flag, missing required on pageName/listDisplayName, wrong site-picker mimeType, dead subBlock refs - Add 8 new tools within already-granted scopes for CRUD completion: delete_list_item, get_list_item, delete_page, update_page, publish_page, download_file, delete_file, get_drive_item - Add opt-in stripAuthOnRedirect option to secureFetchWithPinnedIP so the SharePoint file-download route doesn't resend the bearer token to the preauthenticated redirect target (default off, no behavior change elsewhere) - Dedupe read-only list-item field sanitization into a shared utils helper * fix(sharepoint): encode siteId consistently and fix create_page precedence - URL-encode siteId/groupId everywhere it's interpolated into a Graph request path (Graph site IDs like "host,guid,guid" contain commas that must be encoded) - addresses Cursor Bugbot findings on update_page, delete_page, publish_page, and applies the same fix consistently across every other sharepoint tool for consistency - Fix create_page's site-resolution precedence (was siteSelector before siteId, inconsistent with every sibling tool) * fix(sharepoint): escape HTML in page content and stop fallback from throwing - create_page/update_page only escaped quotes when building innerHtml; add a shared escapeHtml() helper that also escapes &, <, > so page content with angle brackets/ampersands doesn't corrupt the canvas layout - add_list_items' fields fallback (sanitized re-derivation when Graph's response omits fields) could throw on a malformed fallback input after the item was already created successfully; catch and fall back to undefined instead of failing an already-successful response * fix(sharepoint): scope columnDefinitions->pageContent mapping to create_list Both V1 and V2 tools.config.params mapped columnDefinitions onto pageContent whenever columnDefinitions was truthy, with no operation check. Stale list-column JSON left in block state after switching to create_page/ update_page would silently replace the user's intended page text. Gate the mapping on operation === create_list (V1) / sharepoint_create_list (V2). * fix(sharepoint): cap download-file content fetch at MAX_FILE_SIZE Greptile flagged the content fetch as unbounded - a large file would buffer entirely in memory before base64-encoding into the JSON response. Pass maxResponseBytes: MAX_FILE_SIZE (100MB, same constant used by the upload path) to secureFetchWithPinnedIP so oversized files reject early with a clear PayloadSizeLimitError instead of exhausting memory. * fix(sharepoint): close V1 block input/output gaps and encode upload URLs Final validation pass (4 parallel audit agents against live Graph docs) surfaced remaining gaps: - apps/sim/app/api/tools/sharepoint/upload/route.ts: siteId/driveId were not encodeURIComponent-ed in the Graph upload URL, unlike every other sharepoint route/tool - same encoding-gap class fixed everywhere else - read_page.ts: transformResponse recomputed siteId with a raw `||` in 3 spots instead of the optionalTrim(...) || optionalTrim(...) || 'root' precedence used by request.url and every sibling tool - SharepointBlock (V1, legacy/hidden-from-toolbar but still executable for existing saved workflows): listItemFields and listItemId were missing `required` for update_list despite the tool requiring them; maxPages/groupId/includeColumns/includeItems/nextPageUrl subBlocks were entirely absent, making those tool params unreachable from the UI; block-level outputs were missing site/pages/content/totalPages/ nextPageUrl/lists/skippedFiles/skippedCount/errors even though the underlying tools return them - V2 already covered all of these |
||
|
|
acece910b4 |
fix(supabase): remove non-functional SQL introspection, harden storage encoding, add missing endpoints (#5371)
* fix(supabase): remove non-functional SQL introspection path, harden storage URL encoding, add missing storage endpoints
- introspect.ts no longer attempts raw SQL via a nonexistent PostgREST
RPC endpoint (always failed); now uses the OpenAPI-spec path directly
with honest heuristic/best-effort documentation for PK/FK/index fields
- storage tools now trim + URL-encode bucket/path segments before
building request URLs (download, list, get_public_url,
create_signed_url, delete_bucket, delete, and the upload API route)
- storage_create_signed_url guards against a missing signedURL field
in the response instead of silently building a broken URL
- add supabase_storage_create_signed_upload_url, supabase_storage_update_bucket,
and supabase_storage_empty_bucket tools + block wiring
- change insert/upsert `data` param type from 'array' to 'json' to match
actual accepted shapes (array or single object)
- bump tool versions to semver (1.0 -> 1.0.0)
- rewrite a BlockMeta template that implied unsupported Supabase Auth
Admin user-provisioning
(cherry picked from commit 52b655acf59bace806c75c06b4b2cfaebe4b6781)
* fix(supabase): address review feedback on update-bucket, signed upload url, and introspect
- storage_update_bucket now fetches the bucket's current config first
and only overrides isPublic/fileSizeLimit/allowedMimeTypes when the
caller explicitly provides them, instead of silently forcing
public: false on every update (the Storage API's PUT is a full
replace, not a patch)
- storage_create_signed_upload_url and storage_empty_bucket now check
response.ok before surfacing an error, so a non-2xx response reports
Supabase's actual error instead of a misleading parse-side message
- introspect.ts drops the spurious ?select=* query param from the
OpenAPI spec request (meaningless on the root spec endpoint)
(cherry picked from commit 557e4074594464262b2f631ca66272bf60f027f8)
* fix(supabase): tri-state bucket visibility on update, empty-string param coercion, introspect schema header
- introspect.ts now sends Accept-Profile when a schema param is given,
matching the convention used by the other DB tools, so schema-scoped
introspection actually reads from that schema's spec
- storage_update_bucket.ts treats an empty-string param the same as
"not provided" (e.g. an untouched file size limit input no longer
coerces to 0)
- the shared "Public Bucket" dropdown always sent an explicit true/false
for storage_update_bucket (its default masked "not touched"), which
could still flip a public bucket private; added a dedicated
"Keep Current / True / False" control for the update operation so
omitting a choice genuinely omits the isPublic override
(cherry picked from commit c80567b980e9a547b892a222cebee2bd03d89420)
* fix(supabase): check response.ok before parsing storage_create_signed_url
Matches the pattern already applied to the new signed-upload-url tool
this session — a non-2xx response now surfaces Supabase's actual error
message instead of the generic "did not return a signed URL path" one.
(cherry picked from commit 9f40427dd80ec21b4af1e85c9c8218fd961d6931)
* fix(supabase): correct download param semantics, echoed path field, and schema hint mismatch
Found by a second, per-tool parallel validation pass against live Supabase/PostgREST docs and source:
- storage_get_public_url.ts and storage_create_signed_url.ts sent
download=true literally, which Supabase's Storage API treats as a
filename override (renaming the downloaded file to "true") rather
than a boolean flag; forcing a download while keeping the original
filename requires an empty download= value
- storage_create_signed_url.ts also sent download in the POST body,
which the sign endpoint ignores entirely — forcing download only
works as a query param on the returned URL
- storage_create_signed_upload_url.ts read a `path` field from the API
response that doesn't exist there; it now echoes the caller-supplied
path, matching the official storage-js client
- introspect.ts's nullable heuristic didn't disclose that a NOT NULL
column with a default is misreported as nullable (PostgREST omits it
from the OpenAPI required list in that case); documented alongside
the other already-disclosed heuristics, and removed a tautological
schema-filter check left over from an earlier revision
- insert.ts/upsert.ts's `data` param reverted from 'json' back to
'array': the 'json' type mapped to an LLM-facing JSON-schema type of
'object', which contradicted the param's own description ("array of
objects or a single object")
|
||
|
|
943ee27686 |
feat(langsmith): add run update, get run, and feedback tools (#5363)
* v0.6.29: login improvements, posthog telemetry (#4026) * feat(posthog): Add tracking on mothership abort (#4023) Co-authored-by: Theodore Li <theo@sim.ai> * fix(login): fix captcha headers for manual login (#4025) * fix(signup): fix turnstile key loading * fix(login): fix captcha header passing * Catch user already exists, remove login form captcha * feat(langsmith): add run update, get run, and feedback tools - add langsmith_update_run (PATCH /runs/{id}) to complete the create-then-patch tracing lifecycle - add langsmith_get_run (GET /runs/{id}) to read a run back - add langsmith_create_feedback (POST /feedback) to attach scores/corrections to runs - wire all three into the LangSmith block, reusing shared subBlocks across operations - fix feedback-capture template to use real feedback API instead of faking it via tagged runs - switch manual Object.fromEntries filtering to filterUndefined per repo convention * fix(langsmith): reject non-numeric feedback score instead of silently sending null Number() on an invalid score string produces NaN, which serializes to JSON null and would still be sent to LangSmith. Throw instead, matching the existing parseJsonValue error pattern. * fix(langsmith): harden error handling and validation on new run tools - update_run, get_run, create_feedback now check response.ok before parsing/returning, matching the cloudwatch/zoom convention instead of silently returning success on a 4xx/404 - block outputs schema now exposes inputs/outputs for Get Run - update_run requires at least one field to patch, matching the batch-ingest guard for post/patch * fix(langsmith): align get_run/update_run outputs and narrow feedback score type - get_run now also outputs runId (alias of id) so workflows can read the run identifier consistently across all operations on the block - update_run now parses and surfaces the response message instead of discarding the body entirely, matching create_run's pattern - narrow LangsmithCreateFeedbackParams.score to number — the tool param is declared as JSON-schema 'number' and the block's parseScore never produces a boolean, so the wider type was dead and misleading * fix(langsmith): reject empty-string patch fields and empty PATCH bodies - block mapper now normalizes blank name/end_time/status/error inputs to undefined instead of forwarding empty strings, which would clear those fields on the LangSmith run - update_run tool now throws if the filtered PATCH body is empty, guarding direct/programmatic tool calls that bypass the block's own "at least one field" check * fix(langsmith): normalize empty-string patch fields at the tool layer too Direct/agent tool calls bypass the block's own emptyToUndefined guard, so update_run now normalizes blank name/end_time/status/error itself before filtering, matching the block-level fix. --------- Co-authored-by: Theodore Li <theodoreqili@gmail.com> Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com> Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com> Co-authored-by: Theodore Li <theo@sim.ai> |
||
|
|
a267a95809 |
fix(vercel): align integration with live Vercel REST API docs (#5370)
* fix(vercel): align integration with live Vercel REST API docs - add missing teamId/slug scoping to 29 tools (deployments, checks, projects, env vars) - add DNS SRV/HTTPS record support (nested srv/https objects) to create/update_dns_record - add decrypt/gitBranch filters to get_env_vars, autoUpdate to rerequest_check - add redirect param to create_alias, comment to create_dns_record - add missing customNameservers/userId/teamId/transferStartedAt fields to list_domains output - wire pagination filters (limit/since/until/search/app) into block subBlocks for list_deployments, list_teams, list_team_members, list_dns_records, list_project_domains - wire previously-unexposed tool params into block UI: withGitRepoInfo, gitSource, forceNew, externalId, rerequestable, output, direction, follow - fix duplicate projectId subBlock id collision between list_deployments and other project-scoped operations - fix teamId scope field incorrectly hidden for check operations * fix(vercel): address review findings on DNS/deployment param handling - fix withGitRepoInfo 'No' dropdown sending withGitRepoInfo=false instead of omitting the param - remove redundant duplicate 'Forward' option from eventsDirection dropdown - fix mxPriority being silently dropped in update_dns_record when record type is left unset * fix(vercel): fix pagination token type mismatch and clarify DNS update UX - fix list_projects nextFrom being stored as a number despite string typing, which threw TypeError on .trim() when chained into a follow-up list_projects call - clarify that the DNS update Value field has no effect on existing SRV/HTTPS records unless Record Type is explicitly reselected * fix(vercel): fix zero-value SRV/HTTPS/MX numeric fields being dropped - SRV weight/port/priority, HTTPS priority, and MX priority use truthy checks that silently drop legitimate 0 values; switch to explicit empty/null checks - add missing MX Priority field to create_dns_record block UI (tool already required it for MX records but there was no way to set it) * fix(vercel): fix stale cross-operation field leaks in block params - fix list_deployments/create_deployment/update_check/list_team_members/update_dns_record/update_env_var conditionally spreading into keys that collide with unrelated operations' non-destructured literal subBlock fields (projectId, deploymentId, target, name, search) - a stale value left over from switching operations in the UI would silently leak into the wrong tool call since the conditional spread only overrides when the current operation's own field has a value - fix now always assigns these keys directly so they deterministically override any stale base value * feat(vercel): close remaining input/output completeness gaps - add missing slug (team-slug) param to 13 domain/DNS/alias tools that were skipped in an earlier fix pass, matching the pattern used everywhere else in this integration - add rootDirectory, nodeVersion, devCommand params to create_project/update_project (verified against Vercel's live OpenAPI spec — high-value fields for monorepo support and runtime pinning) - surface rootDirectory/nodeVersion in get_project/list_projects outputs, since the API already returns them - wire all new fields into the block UI as advanced fields * fix(vercel): fix remaining No->false truthy-string dropdown bugs - checkRerequestable, checkAutoUpdate, envVarsDecrypt dropdowns used id: 'false' for their No option (a truthy non-empty string), causing the conditional spread to always fire and explicitly send false instead of omitting the param - swept the whole file for this pattern; checkBlocking correctly keeps 'false' since it's a required field that's always sent directly, not conditionally * fix(vercel): fix silent project-rename leak in update_project - update_project had no dedicated rename field and just returned base directly, so a stale value from create_deployment's unrelated 'name' subBlock (Project Name for the deployment) could silently flow through and rename a project on update - add a dedicated 'New Project Name' field for update_project, wired with a direct override so it always takes precedence over any stale leaked value |
||
|
|
0507acf2dd |
fix(amplitude): correct wire formats and add funnels/retention analytics (#5355)
* fix(amplitude): validate integration against API docs, add funnels/retention - Fix Identify/Group Identify sending JSON instead of the form-urlencoded body Amplitude requires - Fix Send Event using snake_case product_id/revenue_type instead of Amplitude's camelCase productId/revenueType - Fix Get Revenue parsing a response shape that never matched the real Revenue LTV API - Add EU data residency support across all tools - Add missing filters/formula/segment params to Event Segmentation, groupBy/segment to Active Users and Revenue - Add first_used/last_used to User Activity output, non_active/flow_hidden to List Events output - Add real-time/hourly interval options to Event Segmentation - Add Funnels and Retention tools for conversion and retention analysis - Harden JSON-shape validation across funnels/segmentation/retention (fail loudly on malformed or partial input instead of silently degrading) - Expose every tool output field (user_profile, send_event, user_search) on the block so nothing is unreachable downstream - Update brand colors to current Amplitude guide * fix(amplitude): validate retention brackets, require formula param, add segmentation 2nd group-by - Retention now validates retentionBrackets as a JSON array and requires it when retentionMode is "bracket", matching the block UI's requirement - Event Segmentation now throws if metric is "formula" but no formula is provided, matching the block UI's requirement - Event Segmentation now supports a documented second group-by property via groupBy2/g2 - Corrected Get Active Users' group-by copy — Amplitude's docs don't document a second-property syntax for /api/2/users the way they do for segmentation's g2, so the field no longer overpromises "max two" |
||
|
|
59d6b8a62e |
fix(onepassword): validate integration against API docs, add file downloads (#5365)
* fix(onepassword): validate integration against API docs, add file downloads
- add onepassword_get_item_file tool + route for downloading item file
attachments (SDK items.files.read / Connect files/{id}/content), backed
by newly-exposed item.files metadata on get/create/replace/update item
- fix update_item JSON Patch applying array indices instead of 1Password's
documented field-ID addressing (/fields/{fieldId}/...), which silently
dropped field edits in Service Account mode
- fix Service Account mode's list-vaults/list-items filter to honor SCIM
`eq` exact-match semantics instead of always substring-matching
- expand the create-item category dropdown from 9 to 19 real, creatable
1Password categories (was missing SOFTWARE_LICENSE, EMAIL_ACCOUNT,
MEMBERSHIP, PASSPORT, REWARD_PROGRAM, DRIVER_LICENSE, BANK_ACCOUNT,
MEDICAL_RECORD, OUTDOOR_LICENSE, WIRELESS_ROUTER, SOCIAL_SECURITY_NUMBER)
- replace the block's single opaque `response: json` output with typed,
per-operation output fields matching repo convention
- remove incorrect password-masking on the Vault ID field (not a secret)
- re-export tool types from the onepassword barrel
* fix(onepassword): honor SCIM attribute name in filter matcher
matchesFilter always compared against name/title regardless of the
attribute named in the eq expression, so `id eq "..."` incorrectly
matched against the display name instead of the id.
* fix(onepassword): close output-parity and doc-string gaps from final audit
- restore a deprecated no-op 'response' output so pre-existing saved
workflows referencing it fail soft (empty) instead of hard-erroring
now that per-operation outputs replace it
- add missing block outputs (urls, favorite, version, state,
lastEditedBy) for get/create/replace/update item so all real
FULL_ITEM fields are discoverable as <Block.field> references
- hide Connect Server credential fields for Resolve Secret (Service
Account only) instead of leaving them selectable and silently ignored
- correct two doc-string enum lists that advertised values the API
doesn't return (vault type TRANSFER, item state DELETED)
* fix(onepassword): fix silent data loss in update_item (Service Account mode)
update_item applied user JSON Patch ops (documented/typed against the
Connect-shaped vocabulary get_item returns: label/type/section.id)
directly onto the raw SDK item, whose vocabulary differs (title/
fieldType/sectionId, and SDK category enum strings vs Connect's
SCREAMING_SNAKE_CASE). Most patches beyond /title, /tags/-, and
/fields/{id}/value silently no-opped or could corrupt the item while
still reporting success.
Extracted the Connect->SDK item conversion already used by replace_item
into a shared connectItemToSdkItem helper. update_item now normalizes
the fetched item to Connect shape, applies patches to that, then
converts back before calling items.put() -- matching create/replace's
existing translation pattern.
Found via an adversarial final-verification pass that traced concrete
patch operations by hand against the SDK's actual field vocabulary.
* fix(onepassword): preserve field metadata and empty-title fallback
connectItemToSdkItem rebuilt every field as a bare object, dropping
SDK-only metadata (e.g. password-generation details) that a raw
patch/replace previously left untouched. Now merges onto the existing
SDK field by id before applying the translated properties, and only
starts fields bare when they're genuinely new.
Also restored the || (not ??) fallback on title to match replace_item's
prior behavior of treating an explicitly empty title as "not provided".
|
||
|
|
f658e6dc73 |
fix(tailscale): align tool coverage and outputs with the Tailscale API (#5366)
* fix(tailscale): align tool coverage and outputs with the Tailscale API
- fix list_users profilePicUrl field name (API returns lowercase, was always null)
- add nodeId, keyExpiryDisabled, expires to device outputs
- quote the If-Match header value on ACL updates per API spec
- add set_acl, expire_device_key, suspend_user, delete_user tools
- add wandConfig to dnsServers/searchPaths block fields
- expand BlockMeta skills/templates for ACL and key-expiry workflows
* fix(tailscale): remove phantom magicDNS field from list_dns_nameservers
The GET /tailnet/{tailnet}/dns/nameservers response only returns
{dns: string[]} per the API spec — magicDNS is not part of this
endpoint's response and was always silently false.
* fix(tailscale): extend ToolResponse in expire_device_key response type
Matches the pattern used by the other new tools in this PR
(delete_user, suspend_user).
* fix(tailscale): list_auth_keys now returns all tailnet keys
GET /tailnet/{tailnet}/keys silently scopes to the caller's own
keys unless all=true is passed, contradicting the tool's stated
purpose of listing all auth keys in the tailnet.
|
||
|
|
5966e5cf5a |
feat(similarweb): add page views tool, fix paid referrals field mismatch (#5354)
* feat(similarweb): add page views tool, fix paid referrals field mismatch - Add similarweb_page_views tool (Total Page Views, Desktop & Mobile) - Fix paidReferrals in website overview: API Lite response key is literally "paid _referrals" (space before underscore), not caught by the existing fallback chain, so it always resolved to null - Move startDate/endDate/mainDomainOnly to advanced mode * fix(similarweb): accept both page_views key spellings in page views response Cursor Bugbot and Greptile both flagged the response field as page_views by convention, but SimilarWeb's own docs example response uses pages_views for this specific endpoint. Accept both spellings defensively so the tool works regardless of which is actually returned. |