Skip to content

testing workflow of pr #516

Closed
royceP2 wants to merge 413 commits into
version-v4-mainfrom
main
Closed

testing workflow of pr #516
royceP2 wants to merge 413 commits into
version-v4-mainfrom
main

Conversation

@royceP2
Copy link
Copy Markdown
Collaborator

@royceP2 royceP2 commented May 27, 2026

Summary

Brief description of what this PR does and why.

Fixes #(issue)

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

How has this been tested? What should reviewers focus on?

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

TheodoreSpeaks and others added 30 commits May 5, 2026 19:59
* fix(md-render): fix markdown rendering in file viewer

* fix(md-render): use Children.map in pre handler for robustness

* fix(md-render): add not-italic to fallback code element

* fix(md-render): fix cloneElement type error and include tables improvements

- Fix TypeScript build error: type isValidElement<Record<string, unknown>> so cloneElement accepts data-block prop
- Column sidebar: use CSS min() for responsive width instead of fixed 400px
- Table: boolean cell toggle only fires when clicking the checkbox element directly (via data-boolean-cell-toggle), not anywhere on the cell
- Table: double-clicking a boolean cell no longer opens edit mode
- Table: move row-select mousedown to the <td> to widen the hit target
- Table: run/stop button prevents row-select on mousedown
- Table: SelectAllCheckbox made keyboard-accessible; checkbox is pointer-events-none
)

* chore(docs): upgrade fumadocs to latest minor versions

- fumadocs-core: 16.6.7 -> 16.8.5
- fumadocs-ui: 16.6.7 -> 16.8.5
- fumadocs-mdx: 14.2.8 -> 14.3.2
- fumadocs-openapi: 10.3.13 -> 10.8.1
- migrate deprecated sidebar.tabs to top-level tabs prop
- fix pre-existing typo (slots.paremeters) surfaced by stricter openapi types

* fix(docs): revert sidebar.tabs migration to keep deploy compat

The top-level tabs prop is only on fumadocs-ui 16.7+; deploy env was
still resolving an older type and failing typecheck. sidebar.tabs is
deprecated but still functional — keep it for now.
…ok (simstudioai#4440)

* feat(enterprise): add data drains for continuous export to S3 / webhook

* chore(data-drains): regenerate migration on top of staging + bump route baseline

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

* docs(data-drains): clarify retention pairing is user-coupled, not enforced

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

* fix(data-drains): preserve explicit forcePathStyle=false + reserve x-sim-signature

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

* test(data-drains): drift guard ensures every webhook header is reserved

Asserts that any header buildHeaders writes is rejected when reused as a
custom signatureHeader. Adding a new metadata header without mirroring it
into RESERVED_SIGNATURE_HEADER_NAMES now fails CI.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…achine (simstudioai#4450)

* fix(terminal): terminal console update for child spans"

* address comments

* fix hitl state machine

* address comments

* address greptile
…simstudioai#4461)

* improvement(confluence): expand scopes, persist canonical mode toggle

* improvement(confluence): memoize persisted canonical modes parse

* fix(confluence): paginate space selector dropdown

Confluence v2 spaces endpoint caps at limit=250 per page. The selector
endpoint was making one request and silently dropping every space past
the first page, which is why some spaces only worked when entered as a
manual spaceKey. Now follows _links.next cursor up to 20 pages (5000
spaces).

* fix(confluence): include archived spaces in selector dropdown

Confluence v2 /spaces defaults to status=current and the status param
is a single-value enum, so archived spaces never surface. They synced
fine when entered manually as a spaceKey because the connector looks
up spaces via ?keys=<key> which ignores status. Now fetches current
and archived in parallel and tags archived ones in the dropdown label.

* improvement(confluence): stream paginated space selector results

Bake pagination support into the selector abstraction via an opt-in
fetchPage definition so dropdowns populate progressively instead of
blocking on a full page-walk. Confluence spaces now stream current
then archived in a single cursor sequence.
* fix(md): file streaming patch preview

* address comment
* fix(security): block IPv4-compatible IPv6 SSRF bypass

* fix(security): also block IPv4-compatible IPv6 with Class E embedded IPv4

* fix(security): correct RFC1918 test label for IPv4-compat IPv6
)

* fix(tables): decouple master checkbox from cell-range, add allRowsSelected flag

Master checkbox detached from gutter selection state when rows or columns
changed after Cmd+A: the predicate matched normalizedSelection bounds
exactly (endRow === rows.length-1, endCol === displayColumns.length-1),
so any post-selection growth flipped it false while the cell-range
overlay still painted every row checked.

Replace the structural two-branch predicate with an explicit
allRowsSelected flag plus a uniform set-membership check. handleSelectAllRows
sets the flag in O(1); handleRowToggle materializes checkedRows when
toggling out of "all" mode. Bulk-op read sites (delete, copy, cut,
selectedRowCount) honor the flag.

Decouple gutter checkbox from cell-range drag: dragging cells no longer
fills gutter checkboxes — they reflect explicit row-selection intent
only, matching Sheets/Airtable. Cell-range overlay still paints cells.

* refactor(tables): row selection as discriminated union

Collapse `checkedRows: Set<string>` + `allRowsSelected: boolean` into a
single `RowSelection = { kind: 'none' | 'some' | 'all' }`. Impossible
states (all + non-empty Set) become unrepresentable; predicates like
`rowSelectionIncludes` and `rowSelectionIsEmpty` replace ad-hoc checks at
every read site.

* fix(tables): clear row selection after context-menu delete

handleContextMenuDelete dispatched the delete but left rowSelection at
its prior 'all' or 'some' state. After rows clear and a new row arrives
(realtime, undo, append), rowSelectionIncludes returned true for it,
rendering it checked and flipping the master checkbox back on.

* chore(tables): address review nits on row selection refactor

- guard selectedRowCount 'all' branch on contextRow membership in rows
- restore blank line between row-selection helpers and constants

* chore(tables): rename rowSelectionChanged to cellRangeRowChanged

The helper compares NormalizedSelection (cell-range) state for a given
row, not RowSelection. The old name collided with the new row-selection
discriminated union and read ambiguously.

* fix(tables): guard context-menu delete on stale rows, preserve selection on cancel

- Guard the kind='all' branch on contextRow membership in currentRows
  (matches the same fix applied to selectedRowCount), so a context menu
  on a stale row no longer deletes the entire table.
- Drop the eager rowSelection clear at modal-open time. The modal's
  onSuccess already calls handleClearSelection after the mutation
  resolves, so the post-delete invariant still holds; if the user
  cancels, the selection is now preserved.
…ents (simstudioai#4468)

* fix(mothership): enforce ownership check on workflow resource attachments

* fix(mothership): fix table and knowledgebase BOLA in resource attachment resolution

* fix(mothership): apply workspace scope to table in processContextsServer

* fix(mothership): verify workspace membership before resolving workspace branch

* fix(data-drains): use const for timeoutId in sleepUntilAborted

* fix(test): mock db.select and drizzle and for workspace permissions check

* fix(mothership): always derive workspace from workflow record in workflow branch
…imstudioai#4471)

Catch Postgres 23505 on insert/update so concurrent name conflicts
return a clean 409 instead of a 500. The data_drains_org_name_unique
index already prevents duplicate rows; this just improves the UX.
…ering upgrades, data drains, security hardening, paginated dropdowns
…ction block code (simstudioai#4223)

* v0.6.29: login improvements, posthog telemetry (simstudioai#4026)

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

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

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

* fix(signup): fix turnstile key loading

* fix(login): fix captcha header passing

* Catch user already exists, remove login form captcha

* fix: use context variables for block outputs in function block code

When a function block references another block's output via <BlockA.result>,
the executor previously embedded the full value as a JavaScript literal
directly in the code string. For large outputs (>50 KB), this caused the code
string to exceed the terminal console display limit, making inputs appear
truncated or replaced with { __simTruncated: true } in the UI.

Instead, block output references in function block code are now stored as
named global variables (__blockRef_N) in the isolated VM context. The code
string only contains the compact variable name, keeping it small regardless
of the referenced value size.

Loop/parallel/env/workflow references are still inlined as literals since
the API route has no way to resolve them independently.

The _runtimeContextVars key is filtered from sanitizeInputsForLog so it
does not appear in execution logs or SSE events.

Pre-resolved context variables are merged with any variables produced by
the API route resolveCodeVariables, with executor values taking precedence.

Fixes simstudioai#4195

* fix: address Cursor and Greptile bot review comments

- Pass preResolvedContextVariables through to shellEnvs for Shell language
  (Cursor: shell loses pre-resolved block refs, executes against undefined vars)
- Remove duplicate CodeExecutionOutput interface declaration
  (Cursor + Greptile: dead duplicate declaration in tools/function/types.ts)
- Deduplicate identical block references in resolveCodeWithContextVars so the
  same <BlockA.result> reused multiple times shares one __blockRef_N slot
  (Greptile P2: avoid duplicating large payloads across the wire)

* fix: shell block references and complex env value serialization

Two follow-ups to the function-block context-variable refactor:

- resolveCodeWithContextVars now emits `$__blockRef_N` for shell
  function blocks so the script dereferences the env var injected
  by the executor. Other languages still receive the bare identifier.
- The function-execute route now JSON-stringifies non-primitive
  values when building shell env vars, replacing the previous
  `String(v)` call that produced `[object Object]` for objects/arrays.

Co-Authored-By: Octopus <liyuan851277048@icloud.com>

* fix lint

* review pass

* ignore shell comments

* update contract

* fix tests

---------

Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Theodore Li <theodoreqili@gmail.com>
Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: octo-patch <octo-patch@github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
…ion (simstudioai#4474)

* fix(function): validate custom tool param keys before code interpolation

* fix(function): exclude JS reserved words from param key injection guard
* chore(deps): upgrade next.js to 16.2.4

- Bump next and @next/env to 16.2.4 across root, apps/sim, apps/docs
- Replace next-runtime-env's env() helper (calls unstable_noStore(), rejected by Next 16.2 outside request scope) with a direct window.__ENV / process.env getter
- Add export const dynamic = 'force-dynamic' on landing /privacy and /terms pages so NEXT_PUBLIC_* runtime env reads aren't baked at build

* fix(whitelabel): force dynamic rendering for manifest.ts

Without this, NEXT_PUBLIC_BRAND_* values are baked into the manifest at build time. Pairs with the next-runtime-env removal in the prior commit, restoring Docker runtime injection for whitelabel deployments.

* fix(oauth): wrap consent page useSearchParams in Suspense

Next 16.2's stricter prerender check fails the build when useSearchParams() is used without a Suspense boundary. Splits the client component into an outer wrapper and inner body.

* fix(whitelabel): force dynamic rendering for landing segment

Client components in (landing) (e.g. Navbar) read NEXT_PUBLIC_BRAND_* via getEnv. Without this, SSR prerender would bake the build-time process.env values into HTML, mismatching window.__ENV after hydration in Docker runtime-env deployments. Cascades to all landing routes via the layout.

* revert(whitelabel): drop force-dynamic from landing layout

Cascading force-dynamic neutered dynamicParams = false + generateStaticParams on /blog/[slug], /integrations/[slug], /models/[provider], /models/[provider]/[model] — killing static prerender for SEO-critical pages. The hydration concern only materializes for whitelabel Docker deployments where build-time and runtime NEXT_PUBLIC_BRAND_* differ; those deployments can set the vars at build instead. Keeping force-dynamic on /privacy, /terms, and /manifest where it actually matters.

* fix(prerender): wrap useSearchParams callsites for Next 16.2

Next 16.2 fails the build when a client component using useSearchParams() is statically prerendered without a Suspense boundary.

- Wrap landing Navbar in Suspense (imported by /oauth/consent and other pages)
- Add force-dynamic to reset-password, invite/[id], and unsubscribe pages whose client bodies call useSearchParams

* fix(navbar): preserve SSR HTML, drop Suspense bailout

Reading useSearchParams() forced a Suspense fallback that emitted no navbar HTML during SSR — leaving crawlers and no-JS users without nav. The 'home' query param only affects client-side link targets, so read it from window.location in an effect after hydration. Restores full SSR navbar markup.

* chore: trim verbose comments in next.js upgrade

The force-dynamic export name is self-documenting; the remaining env.ts comment is tightened to the essential WHY (why we don't use next-runtime-env's helper).
…imstudioai#4477)

* fix(agiloft): correct response parsing, add EWGetChoiceLineId tool

* fix(agiloft): address PR review feedback
- drop redundant turbopack config (Next 16 defaults)
- remove lucide-react/date-fns from optimizePackageImports (built-in defaults)
- enable turbopackFileSystemCacheForBuild for warm CI builds
- disable poweredByHeader
- swap actions/cache for Blacksmith sticky disk on .next/cache
…simstudioai#4470)

* feat(emailbison): block, tools

* type improvments

* typecheck issue

* add email bison trigger, cleanup sharepoint block

* address comments

* fix tests

* error on partial upload failures
…rrors (simstudioai#4479)

* fix(office-excel): support Office.js add-in embed and surface Graph errors

* fix(office-excel): delegate to parseGraphErrorFromData and handle array embed param
…ink-preview rules (simstudioai#4480)

* improvement(seo): restore explicit AI/search bot allow-list and add link-preview rules

* fix(seo): correct xAI UA strings, drop Bravebot, block /playground/ and /w/ from link-preview bots

* fix(seo): drop unverified Grok UAs, correct DeepSeekBot and ImagesiftBot tokens

* fix(seo): re-add Bravebot to allow-list per Brave Search docs

* improvement(seo): drop redundant named AI/search bot allow-list

* chore(seo): trim verbose comments in robots.ts
* improvment(executor): reserved keyword errors

* address comments and make error messages for func execute make sense block ref accs
…oai#4481)

* fix(security): xlsx CVE bump and bundled security hardening

* fix(stripe): use configured secret key for SDK init

Avoids leaving a recognisable placeholder string in heap dumps and
error serialisations. Webhook verification remains a purely local
HMAC operation; the SDK's constructor key is unused by it.

Addresses Greptile feedback on simstudioai#4481.

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

* fix(stripe): use static Stripe.webhooks for verification

Avoids instantiating a Stripe client just to access constructEvent.
The webhook signing secret is per-trigger (user-provided whsec_…) and
unrelated to our billing STRIPE_SECRET_KEY, so coupling them was wrong.
Stripe.webhooks is exposed as a static — no client, no API key needed.

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

* fix(ci): revert client-bundled tools to avoid .server import in client

* fix(security): collapse 403 to 404 on v1 detail-by-ID routes

* chore(security): remove unused validateAgiloftInstanceUrl helper

* fix(security): bump minimatch + clean up scripts/ workspace

Resolves CVE-2026-27903 (GHSA-7r86-cg39-jmmj) by adding a root-level
minimatch ^10.2.5 override. Also resolves CVE-2026-0969 in next-mdx-remote
(bumped to ^6.0.0).

Cleanup:
- Make scripts/ a proper bun workspace (root workspaces array)
- Remove duplicate scripts/package-lock.json (this repo uses bun)
- Remove redundant scripts/bun.lock (now hoisted to root)
- Remove vestigial scripts/setup-doc-generator.sh
- Slim scripts/package.json to its real deps (glob, yaml)
- Gitignore stray package-lock.json files
- Update scripts/README.md

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…n fixes (simstudioai#4483)

* feat(sap): add SAP Concur integration block and SAP S/4HANA validation fixes

* added

* fix(sap_s4hana): preserve raw Set-Cookie array for CSRF cookie join

SecureFetchHeaders previously collapsed multi-value Set-Cookie headers
with ", ", forcing consumers to re-split via a fragile regex. Cookie
values containing "=" or "," (e.g., Base64 session tokens) could be
misparsed and produce malformed Cookie strings on CSRF-protected
mutations.

Add SecureFetchHeaders.getSetCookie() that returns the raw array, and
update the S/4HANA OData proxy's joinSetCookies to consume it directly.

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

* fix(sap-concur): rename misleading exchange-rate tool, drop unusable refresh_token grant, validate geolocation host

- Rename sap_concur_get_exchange_rate to sap_concur_upload_exchange_rates (POST bulk upload, not GET)
- Remove refresh_token from SapConcurGrantType / Zod enum / block dropdown / docs (no implementation)
- Validate Concur geolocation hostname against SAP_CONCUR_ALLOWED_DATACENTERS

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

* finished

* docs

* fix(docs): escape braces in tool/trigger description prose for MDX

Tool and trigger descriptions can contain URL path placeholders like
{reportId} or JSON-shape hints like { Items, NextPage }. When rendered
as MDX prose (not table cells), these were emitted unescaped and MDX
parsed them as JSX expressions, failing prerender with
"ReferenceError: reportId is not defined".

Escape { and } in the operation-level description and trigger
description renderers, matching the existing escaping in table-cell
descriptions.

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

* fix(sap-concur): align with live API on travel-profile, itineraries, and context types

- list_travel_profiles_summary: rename Status query to Active with 1/0 values, tighten LastModifiedDate format hint
- list_itineraries / get_itinerary: use documented userid_type / userid_value / ItemsPerPage / Page query keys
- create_report_comment: contextType allows MANAGER (move to EXPENSE_READ_CONTEXT_TYPE_OPS)
- get_list_item: drop unused listId from block (tool only needs itemId)
- Tighten description copy on list_expenses/get_itemizations/associate_attendees/remove_all_attendees

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

* fix(sap-concur): correct Cash Advance v4.1 paths, add SCIM filter param

- Update Cash Advance create/get/issue tools from /cashadvance/v4/ to /cashadvance/v4.1/ to match the live API
- Add filter query param to list_users (SCIM v4.1 supports filtering by userName, employeeNumber, externalId)
- Regenerate docs MDX

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

* fix(sap-concur): drop SCIM list_users filter param (not supported on v4.1 GET)

SCIM Identity v4.1 GET /Users does not accept a filter query parameter — filtering
is only supported via POST /Users/.search (already exposed by sap_concur_search_users).

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

* fix(sap-concur): final live-API alignment

Verified against live SAP Concur docs (concur/developer.concur.com preview branch):

- Revert Cash Advance paths to /cashadvance/v4/ (v4.1 endpoints do not exist; live spec is v4)
- Travel Profile v2 summary has no Active/Status query param — drop the filter from tool, types, and block
- Report Comments v4 contextType is TRAVELER or PROXY only (NOT MANAGER) — move create_report_comment + list_report_comments into the TRAVELER/PROXY context group
- Trip v1.1 query keys: userid_type / userid_value / ItemsPerPage / Page (snake/Pascal per docs) — already correct, kept

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

* docs

* fix(sap-concur): restore Cash Advance v4.1 paths

Re-verified against live developer.concur.com docs at /api-reference/cash-advance/v4-1.cash-advance.html — only v4.1 endpoints are documented:
- POST /cashadvance/v4.1/cashadvances
- GET /cashadvance/v4.1/cashadvances/{cashAdvanceId}
- POST /cashadvance/v4.1/cashadvances/{cashAdvanceId}/issue

The /cashadvance/v4/ docs page returns 404. Reverts the prior local rollback in 9ef3a11.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…imstudioai#4411)

- Add pan/zoom/fit controls to mermaid diagrams rendered inline in markdown — same experience as the standalone .mmd viewer
- Wrap inline markdown images in ZoomablePreview with fit-to-container scale
- Allow fit zoom to upscale small diagrams to fill the view (previously capped at 100%)
`turbo prune sim --docker` strips `scripts/` from the pruned output (sim
doesn't depend on it), but the pruned root package.json still listed it
as a workspace, causing `bun install` to fail with "Workspace not found
'scripts'" in the Docker build.

scripts/ is dev-only tooling that runs from the repo root via `bun run
scripts/*.ts`. Its imports (glob, yaml) resolve against the root
node_modules — they're already in root devDependencies.

- Remove "scripts" from root workspaces array
- Delete scripts/package.json (no longer a workspace, manifest unused)

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…udioai#4485)

* fix(workday): correct SOAP service routing and reference types

- create-prehire: route Put_Applicant to Recruiting service (was Staffing, where the operation does not exist)
- assign-onboarding: use WID for Action_Event_Reference (was Background_Check_ID, wrong identifier domain for hire events)
- update-worker block: rewrite labels and wand prompt to match Change_Personal_Information demographic-only scope (prior prompt instructed LLM to emit businessTitle/primaryWorkEmail which the SOAP op rejects)
- enrich opaque JSON output descriptions on worker, workers, organizations, compensationPlans

* fix(workday): correct Date_of_Birth casing in update wand prompt
…ntegrations, robots.txt update, workday hardening
waleedlatif1 and others added 29 commits May 22, 2026 06:40
…mstudioai#4721)

* fix(combobox): show selected values in multi-select trigger label

The collapsed trigger was reading only `selectedOption` (the single-value
path) and falling back to the placeholder when nothing matched, so a
multi-select dropdown with 1+ checked items still rendered "Select one or
more channels" instead of the actual selections.

Added `multiSelectLabel` derived from `multiSelectValues`:
- 1 value  → that label
- 2 values → "A, B"
- 3+       → "A, B +N"

Trigger now prefers `multiSelectLabel` when present and falls back to the
single-select label / placeholder otherwise. Muted-text color also flips
off when multi has any selection.

* chore(kb-connectors): strip redundant field-level descriptions

Removed 41 inline `description:` lines from configFields across 16 connectors
(Slack, MS Teams, GCal, Gmail, Notion, Linear-adjacent, Discord, Dropbox,
Evernote, Fireflies, Google Sheets, Intercom, Obsidian, Outlook, Reddit,
ServiceNow, WordPress, Zendesk). They mostly restated the field title (e.g.
"Channels to sync messages from" under a "Channels" label) and cluttered
the add/edit modal. Field titles + placeholders already communicate intent.

Connector-level `description` (used in the connector picker grid) is
unchanged.

* test(leader-lock): use fake timers to deterministically test follower polling

The "follower does a final read after timeout" test (and the
"follower returns null after timeout" test) relied on real-clock
`setTimeout` and `Date.now()` with very tight bounds (pollIntervalMs=5,
maxWaitMs=9). Any CI scheduler jitter of >4ms would cause the second
in-loop poll to be skipped, the polls counter to end at 2 instead of 3,
and the assertion `expect(result).toBe('late-leader')` to fail.

Switched both tests to `vi.useFakeTimers()` so the schedule is driven by
mocked time advanced via `vi.advanceTimersByTimeAsync`. The intent is
unchanged — verify that the in-loop deadline triggers exactly one
post-deadline last-chance call to `onFollower` — but the assertions no
longer depend on wall-clock timing.

Verified across 5 sequential runs with zero flakes.

* improvement(kb-connectors): restore field descriptions as info-icon tooltips

Restores the 41 field-level `description` lines stripped in fc64421, but
instead of rendering them as inline muted-text paragraphs they're shown via a
small Info icon next to each field title. Hovering or focusing the icon
reveals the description in the existing emcn Tooltip. Keeps the modal layout
tight while preserving the per-field guidance.

Used <button type="button"> as the tooltip trigger (Radix asChild) rather
than <span tabIndex={0}> to satisfy a11y/noNoninteractiveTabindex.
…simstudioai#4723)

* fix(hubspot): fall back to objectType default in selector fetchOptions

useSubBlockStore.getValue returns null for default-valued dropdowns
until the user interacts with them. The properties, pipelines, stages,
and ownerId selectors were treating that as "no selection" and
short-circuiting, so the dropdowns appeared empty even though the
trigger uses 'contact' as the visible default.

Adds resolveSelectedObjectType to mirror the rendered default, so the
selectors fire on first paint with a valid objectType.

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

* fix(hubspot): validate credentialId in selector routes

Mirrors the Gmail/Webflow/Jira selector route security pattern by
rejecting non-alphanumeric credentialId values before authorization
or token refresh.

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

* fix(hubspot): use resolveSelectedObjectType in pipelineId/stageId fetchOptions

Both selectors used inline `?? 'contact'` fallbacks while properties and
targetPropertyName already routed through the resolver. Switch to the
shared helper so custom-object handling stays consistent across every
cascading selector.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…ibuted OAuth lock + typed errors (simstudioai#4722)

* improvement(mcp): post-merge hardening — protocol negotiation + distributed OAuth lock + typed error dispatch

- Inbound MCP server now negotiates protocolVersion per MCP 2025-06-18: echoes
  the client's requested version when supported, falls back to our latest.
  Previously hardcoded to the oldest spec version (2024-11-05).
- withMcpOauthRefreshLock now takes a Postgres advisory transaction lock in
  addition to the in-process Promise chain, so concurrent processes (multi-task
  ECS) serialize on a per-OAuth-row basis. Previously a refresh race across
  processes could rotate a token under another process's feet and force re-auth.
- categorizeError dispatches on McpOauthAuthorizationRequiredError /
  UnauthorizedError / McpConnectionError first, only falling back to substring
  matching for SDK / third-party errors. Adds 502 for connection failures and
  503 for cooldown. Tests cover all four typed cases.
- discoverTools no longer pretends to handle deferred-side-effect rejections
  via a dead allSettled().catch() — each side-effect already self-logs; we just
  swallow per-promise to silence unhandled-rejection warnings.

* fix(mcp): bugbot review on hardening PR

- Replace `as never` cast in negotiateProtocolVersion with `as readonly string[]`
  on the array — preserves TypeScript narrowing on the comparison while
  satisfying the readonly-tuple `.includes()` constraint properly.
- Document the pg_advisory_xact_lock tradeoff: session-level locks
  (`pg_advisory_lock`) would release the connection earlier, but PgBouncer
  transaction-pooling mode breaks them. xact_lock is the correct choice for
  Sim's deployment; if pool pressure becomes real, the comment notes the
  Redlock escape hatch.

* chore(mcp): trim verbose comments + reuse SDK Tool type in McpTool

- McpTool now extends `Pick<Tool, 'name' | 'description'>` from
  @modelcontextprotocol/sdk so name/description fields stay in sync with
  the SDK; serverId/serverName remain Sim-specific additions.
- Drop file-header restatements ("MCP Types - for connecting to external
  MCP servers"), one-line wrapper docstrings ("Get connection status"),
  and narrative comment blocks that just restate visible code.
- Keep only comments that document non-obvious "why" — OAuth refresh-lock
  tradeoff, in-flight dedup key composition, SDK Tool.inputSchema typing,
  preregistered-client semantics, postMessage handshake contract.

* improvement(mcp): swap PG advisory lock for Redis mutex on OAuth refresh

withMcpOauthRefreshLock now uses `coalesceLocally` + Redis acquireLock/
releaseLock with polling — the same primitives backing regular OAuth
refresh (`app/api/auth/oauth/utils.ts`). No more pinning a Postgres
connection for the duration of the SDK's OAuth HTTP refresh.

- In-process dedup: shared promise via `coalesceLocally`.
- Cross-process: Redis SET NX EX mutex; followers poll until the leader
  releases (30s max wait, 100ms poll), then acquire and run fn().
- Each MCP caller still constructs its own client (semantics preserved).
- Falls open when Redis is unavailable — same behavior as the regular
  OAuth refresh code path.

* improvement(mcp): use SDK protocol versions + pool pinned undici agents + cover OAuth lock

- McpClient.SUPPORTED_VERSIONS removed; getVersionInfo() and the inbound
  serve route both import LATEST_PROTOCOL_VERSION / SUPPORTED_PROTOCOL_VERSIONS
  directly from @modelcontextprotocol/sdk/types.js. New protocol revisions
  ship automatically with SDK upgrades.
- pinned-fetch now caches undici Agents in a module-level LRU keyed by
  resolvedIP (max 64). Back-to-back MCP calls to the same server reuse the
  keep-alive connection pool instead of opening fresh TCP + TLS each time.
- New integration tests for withMcpOauthRefreshLock covering: in-process
  dedup via coalesceLocally, cross-process serialization via Redis mutex,
  fall-open on Redis unavailable, lock release on throw, release-failure
  swallow, per-row key isolation.

* fix(mcp): serialize OAuth refresh callers; do not share McpClient

withMcpOauthRefreshLock previously wrapped fn() in coalesceLocally, which
returns the SAME promise (and the same resolved value) to all in-process
callers. fn() returns a stateful McpClient — sharing it meant whichever
caller finished first would disconnect the client while another was still
mid-call, leaving in-flight RPC on a closed connection.

Swap coalesceLocally for a per-row Promise chain: each caller waits for
the previous to settle, then runs its OWN fn() (gets its own client).
Cross-process Redis mutex semantics unchanged.

The "shareable scalar" assumption that makes coalesceLocally correct for
regular OAuth refresh (returns an access token string) does not hold for
MCP, where each caller needs an independent connection.

* fix(mcp): bugbot — TTL watchdog on OAuth lock + don't close evicted pinned agents

- Redis refresh lock now uses a 15s TTL with a watchdog that extends every
  5s while fn() runs. Long-running OAuth refreshes no longer lose the lock
  mid-flight and let another process race the same refresh.
- Pinned-agent LRU eviction no longer calls `agent.close()`. Existing
  `createMcpPinnedFetch` closures hold the dispatcher reference and were
  using a closed Agent after eviction. We drop from the cache and let GC
  release the dispatcher once the last closure dies; undici closes idle
  keep-alive connections via its own internal timeout.
- New tests: watchdog extends while fn() runs and stops once it settles;
  evicted agents are not closed and captured closures still work.

* fix(mcp): throw instead of falling open when refresh lock wait exceeds deadline

When the Redis refresh lock can't be acquired within REFRESH_MAX_WAIT_MS
the previous code ran fn() uncoordinated — but another process can still
be holding the lock (watchdog-extended) and refreshing the same OAuth
row, recreating the exact race the lock prevents.

Throw on deadline. The caller can retry; the Redis-down branch remains
the only path that runs fn() uncoordinated (no coordination is possible
there).

* docs(mcp): restore TSDoc that documented intent on exported types/methods

Earlier comment-trim pass went too far on a few exports — restored
the TSDoc that explained non-obvious "why" decisions:

- SimMcpOauthProviderInit.preregistered: when set, the SDK skips DCR.
- McpServerConfig.userId: required for OAuth; selects which user's
  stored tokens to use.
- McpOauthAuthorizationRequiredError: benign pending state vs failure.
- McpToolsChangedCallback / McpClientOptions: notification semantics,
  DNS-rebinding pinning rationale, OAuth provider contract.
- StoredMcpToolReference / StoredMcpTool: minimal vs extended use.
- McpClient.connect: documents listChanged handler registration.
- McpService.executeTool: documents session-error retry behavior.

Pure-restatement comments ("Disconnect from MCP server") stay trimmed.
… SSRF (simstudioai#4725)

* fix(tools): pin resolved IP in DB connectors to prevent DNS-rebinding SSRF

`validateDatabaseHost` resolved an IP that was then discarded — drivers re-resolved
the hostname at connect time, enabling DNS-rebinding TOCTOU.

- mongodb: pass resolved IP via MongoClient `lookup` option
- mysql: pin TCP socket via `stream` factory; keep hostname for TLS servername
- postgresql: connect to resolved IP; pass `ssl` object with `servername` for SNI
- redis: parse URL explicitly and pass options-only (URL+options breaks override
  due to ioredis's lodash.defaults); pin host and set `tls.servername` for rediss
- neo4j: pin IP for plain `bolt://`; leave `bolt+s`/`neo4j+s` unchanged to keep
  Aura cert validation working (driver hardcodes servername with no override)

* chore(tools): remove explainer comments from DB connector SSRF fix

* fix(tools): add explicit TCP timeout to mysql stream factory

* fix(tools): unify postgres ssl handling to send SNI in preferred mode

* fix(tools): preserve postgres 'preferred' fallback behavior for backward compat

* fix(tools): reject non-numeric Redis URL db segment instead of silently using db 0
* fix(db): disable statement_timeout for migrations

* fix(ci): route migration workflow through guarded migrate.ts
* fix(large-refs): cleanup based on table read

* address comments

* address comments

* bubble up storage ref errors

* cleanup code

* do not attempt blob deletion for infra outage

* cleanup dup helper
…g on load (simstudioai#4732)

* improvement(kb-connectors): align connector UI surfaces and strip redundant description suffix

- Fix icon colors to use --text-icon token across connector cards and modal
- Switch Reconnect/Update access buttons from active to primary variant
- Lift search box surface from --surface-2 to --surface-3 so it's visible against modal bg
- Replace raw <button> elements with emcn Button in base.tsx
- Shrink Connected Sources modal from lg to md
- Strip " to/into/from your knowledge base" from all 27 connector descriptions to prevent overflow

* fix(resource): replace raw pagination button with emcn Button

* fix(resource): prevent permission-gated breadcrumb items from flashing on load

* fix(resource): self-remove keydown listener on Escape and include session loading in isLoading guard
…dioai#4733)

* fix(files): never dedup external URL fetches by path filename

External URL fetches in the file parse route were keyed on the path
filename, so two distinct URLs whose paths ended in `image.png` (e.g.
every Slack clipboard paste) collided in the workspace cache and
returned stale bytes from a prior fetch.

Extracts the fetch + save flow into `fetchExternalUrlToWorkspace` so
the broken dedup pattern can't be reintroduced. The helper always
downloads; `uploadWorkspaceFile` handles name collisions on save by
suffixing (`image.png` -> `image (1).png` -> ...).

* fix(files): distinguish non-member from read-only in workspace-save skip log

Splits the workspace-save skip branch so non-members (permission === null)
log 'user is not a workspace member' instead of the misleading 'lacks
write permission'. Adds a test covering the null case so the relaxed
behavior (vs. the prior route's hard 'File not found') is explicit.
…i for image/video gen, search visibility in cmd-k (simstudioai#4684)

* improvement(media-gen): retire vision block, add hosted key for fal ai for image/video gen

* address comments
…resh token rotation (simstudioai#4735)

* feat(zoom): add KB connector for cloud recording transcripts, fix refresh token rotation

* fix(zoom): trim maxRecordings within page, relax VTT cue-id parsing

* fix(zoom): widen incremental sync overlap to 30 days for late transcripts
* feat: add LiteLLM as AI gateway provider

* fix: add litellm to attachments, provider store, utils, and block guards

* fix: add frontend model discovery pipeline for litellm provider

Add API route, contract, query hook case, and ProviderModelsLoader
entry so litellm models are fetched and synced to the store on
workspace load, matching the vllm/ollama/openrouter/fireworks pattern.

Also fixes defaultModel to empty string and adds litellm/ prefix
early-return in blocks/utils.ts (reviewer feedback).

* fix: remove azureEndpoint fallback from LiteLLM provider

Copy-paste artifact from vLLM provider. LiteLLM should only use
LITELLM_BASE_URL, not fall back to azureEndpoint which could cause
requests to be routed to the wrong server.

* fix(litellm): close audit gaps from PR simstudioai#4644

- byok.ts: add litellm branch to getApiKeyWithBYOK so workflow
  block execution can resolve the proxy key instead of throwing
  "API key is required for litellm ..."
- check-api-validation-contracts.ts: bump route baseline 755 -> 756
  to account for the new /api/providers/litellm/models route
- .env.example: document LITELLM_BASE_URL / LITELLM_API_KEY
- copilot edit-workflow validation: include LiteLLM in the list of
  user-configured prefixed providers shown to the model
- providers/utils.ts: drop stray optional-chain on providers.litellm
  to match the vllm pattern
- lint: apply biome formatting fixes (multi-line if, SVG path,
  multi-line DYNAMIC_MODEL_PROVIDERS)

* fix(litellm): final parity gaps from second audit

- blocks/utils.ts getModelOptions(): include litellm models in the
  combined model dropdown — was previously dropping any
  proxy-discovered models from the agent block model picker.
- get-blocks-metadata-tool.ts mockProvidersState: add litellm bucket
  so the server-side copilot block-metadata fallback can render
  model options when the providers store is not initialized.
- blocks/utils.test.ts: add litellm to mock providers state (initial
  + beforeEach reset) and add a parallel store-bucket guard test
  mirroring the vLLM case.
- providers/utils.test.ts: add parallel getApiKey test for litellm.

* feat(litellm): use official LiteLLM brand icon and color

- icons.tsx: replace the placeholder letterform with the official
  LiteLLM brand mark embedded as a PNG data URI in an SVG image.
- models.ts: set color: #040229 on the litellm provider definition
  to match the brand background.

* chore(litellm): validate /v1/models response with shared schema in initialize()

Match the API route handler — both code paths now run the same
vllmUpstreamResponseSchema.parse() over the upstream /v1/models
JSON instead of a raw type-cast, so malformed upstream payloads
surface a descriptive ZodError instead of a downstream TypeError.

Addresses Greptile review feedback on PR simstudioai#4739.

---------

Co-authored-by: RheagalFire <arishalam121@gmail.com>
…rrect HTTP status (simstudioai#4740)

* fix(api): classify access-denied and sandbox user-code errors with correct HTTP status

* fix(api): gate typed-error message exposure behind publicMessage opt-in

* refactor(api): match NestJS/Spring convention for typed-error message exposure
…ai#4741)

* fix(files): zoom file viewer content, not the browser page

* fix(files): use effect lifecycle for SVG blob URL to survive strict mode
)

* improvement(subflows): orchestration consolidation

* address comments

* fix hitl cases

* address comments

* subflow results output extraction

* hitl fallback case

* more cleanup

* add test

* fix type issue

* add test case for hitl resume

* address comments

* fix test

* fix snapshot for nested subflows
…studioai#4744)

* fix(files): attach wheel listener before paint and guard SVG src

* chore(deps): upgrade turbo to 2.9.14
…plete-sanitization gap (simstudioai#4745)

* fix(zoom): iteratively strip tags to close incomplete-sanitization gap

* test(zoom): cover iterative sanitization in transcript parser
…ping (simstudioai#4746)

* improvement(api): use HttpError base class for typed-error status mapping

* chore(api): drop unreachable runtime check after HttpError instanceof guard
…ort (simstudioai#4742)

* fix(db): disable statement_timeout for migrations

* fix(ci): route migration workflow through guarded migrate.ts

* feat(hosted-keys): add Hunter.io and People Data Labs hosted key support
@royceP2 royceP2 closed this May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants