Commit Graph
288 Commits
Author SHA1 Message Date
Waleed ecbe1919d8 feat(files): inline rich markdown editor (#5133)
* feat(files): inline rich markdown editor

Replace the raw/preview split for markdown files with a Linear-style inline WYSIWYG editor (TipTap/ProseMirror): bubble + slash menus, code-block language picker with Prism highlighting and line-wrap, resizable images (HTML <img>), GFM tables, and frontmatter held byte-exact out of band.

A round-trip preflight gate (decided once per open) falls back to the raw Monaco editor for any file that can't be edited losslessly, so the rich editor never silently corrupts a file.

* fix(files): chain autosave unmount flush after in-flight save

The unmount flush no longer fires a concurrent PUT alongside an in-flight save; it awaits the in-flight save and then writes the latest content sequentially, so an out-of-order completion can't clobber newer edits with a stale snapshot (addresses Cursor Bugbot).

* fix(files): read pasted images from clipboard items, not just files

Some browsers expose a pasted or copied image only via DataTransfer.items (with an empty files list), so screenshot paste was silently ignored. extractImageFiles now falls back to items; moved to a testable module with unit tests (addresses Cursor Bugbot).

* fix(files): destroy round-trip probe editor on serialization error

Wrap the probe serialize() in try/finally so the throwaway Editor is always destroyed even if setContent/getMarkdown throws (addresses Greptile). Adds a test proving PipeSafeTable escapes only interior cell pipes, not structural delimiters.

* fix(resource): hold breadcrumb nav latch across the route swap

scheduleClose fired on the pointer/focus exit that immediately follows a click-to-navigate and was clearing the reopen latch before the route swapped, letting the popover flash back open. The latch is now released by a short timer instead (addresses Cursor Bugbot).

* chore(files): drop platform references and non-essential inline comments

* fix(files): scope inline markdown editor to the files view

The mothership preview was routing streaming markdown through the inline editor path: it showed Monaco during streaming (previewMode fell back to 'editor') and lost the streamed content on the TextEditor→MarkdownFileEditor swap (the TextEditor unmounted before it could reconcile + autosave). The inline rich editor is now opt-in via a FileViewer prop that only the files view sets, so the mothership keeps its raw/preview streaming editor and persists as before.

* fix(mothership): use the inline markdown editor in the chat resource view

Idle markdown in the chat resource view now renders the single-surface inline editor (no raw/split/preview pencil toggle), matching the files view. While the agent streams, FileViewer forces the rendered preview instead of Monaco, and the streamed file persists via the agent's server write + the existing content-query invalidation on tool completion — so the idle editor refetches the persisted content.

* refactor(files): collapse the duplicate raw-editor fallback branch in the markdown gate

* fix(mothership): swap to the inline editor once a file preview finishes streaming

The preview session keeps status='complete' and previewText after streaming ends, so streamingContent stayed defined and the file stuck on the read-only rendered preview. Treat content as streaming only while status==='streaming'; once complete the EmbeddedFile sees no streamingContent and mounts the editable inline editor (which refetches the persisted content). The synthetic streaming-file stays a pure preview.

* Revert "fix(mothership): swap to the inline editor once a file preview finishes streaming"

This reverts commit 25b12e4caa389109c2d5ed7f7d122e35d441f980.

* Revert "fix(mothership): use the inline markdown editor in the chat resource view"

This reverts commit 9430aa7fdc5050d002f2312d31dcf4a255e2de18.

* feat(files): rich markdown editor across files + chat, read-only for unsafe, robust load/save

- chat resource view streams into the rich editor (streamdown while streaming → editable on completion); agent persists server-side, editor never saves mid-stream
- round-trip-unsafe / >128KB markdown renders read-only in the rich editor (no Monaco, no corruption)
- markdown always uses the rich editor (dropped the inline-markdown opt-in flag)
- editor loads content as TipTap's initial content keyed by file id — strict-mode/SSR-safe, no content-sync effect
- fix autosave "Saving…" status suppression under React strict mode
- lock the streamed-file persistence handoff with a state-machine lifecycle test

* chore(files): remove dead code (unused FileViewer logger + EmbeddedWorkflowActions router)

* fix(files): derive markdown round-trip verdict from live content, not a locked stale snapshot

The gate locked isRoundTripSafe on the first post-stream snapshot, which is often the empty create_file buffer before the agent's server write lands — wrongly leaving an unsafe document editable. Derive the verdict from the current content (memoized on the bytes) so canEdit tracks the real payload.

* test(files): guard the rich editor dirty signal — open is never dirty, edits emit

* fix(files): lock the markdown round-trip verdict on opened content, never strand dirty edits

The round-trip-safety verdict now gates editability only at open time — computed once, on the exact
content the editor mounts with, and locked for its lifetime. A dirty document is round-trip-safe by
construction (the editor only emits safe markdown), so the verdict must never flip off mid-edit:
doing so disabled autosave, ⌘S, the toolbar Save and the unmount flush, stranding unsaved edits.
Locking on the opened (reconciled) content also fixes the stale post-stream empty-buffer snapshot,
and lets the redundant MarkdownFileEditor gate (plus its duplicate content fetch) be deleted.

* improvement(file-viewer): reuse shared copy hook, lazy frontmatter split

- code-block: replace hand-rolled copy-with-timeout with shared useCopyToClipboard
- rich-markdown-editor: compute frontmatter split once via lazy ref, drop redundant frontmatterRef
- round-trip-safety: correct stale comments (read-only, not raw editor fallback)

* feat(file-viewer): linked images, typed-link input rule, drag-to-reorder, churn fixes

- image: round-trip linked images/badges via an href attr + custom markdown tokenizer; make
  the image a drag handle so it can be grabbed and reordered
- link-input-rule: convert typed [text](url) to a link on the closing paren (normalized href)
- markdown-paste: render pasted markdown as rich content, guarded against code blocks
- round-trip-safety: behavioral link-count check replaces the static linked-image rejection
- extensions: trim the table serializer's blank lines to stop interior-table whitespace churn

* improvement(file-viewer): Backspace at start of a heading reverts it to a paragraph

Notion-style: ProseMirror's default joins or no-ops at a heading boundary, stranding the
heading style. A second Backspace then merges as usual.

* fix(file-viewer): don't upload pasted/dropped images into a read-only editor

handlePaste/handleDrop ran the workspace image upload without checking editability, so a
read-only doc (canEdit=false or a round-trip-unsafe file) could still trigger an upload.
Guard both on view.editable.

* fix(file-viewer): sanitize linked-image href; drop global leading-newline strip

- image: run the linked-image (badge) anchor target through normalizeLinkHref so a
  javascript:/data: href in a file can't execute on click; the markdown still preserves the
  raw target (file content unchanged)
- markdown-fidelity: the table serializer now trims its own surrounding blank lines, so the
  global leading-newline strip in postProcessSerializedMarkdown is redundant — removing it
  stops clobbering content that legitimately begins with whitespace

* feat(file-viewer): stream agent output directly into the rich editor; add more code languages

- rich-markdown-editor: the TipTap editor is now the only markdown surface. Agent output streams
  into it read-only (synced per chunk, autoscrolled), then the same instance hands off to an
  editable editor on settle — no separate streamdown preview, so no stream→edit flash. The
  round-trip verdict + frontmatter lock when the content settles.
- code-block/code-highlight/detect-language: register Go, Rust, Java, C, C++, C#, Ruby, PHP grammars
  and add detectors, so those blocks highlight and the picker offers them.
- css: style h5/h6 in the prose stylesheet.

* fix(sidebar): hydrate collapse state before paint to stop refresh flash

The collapsed sidebar swaps entire subtrees (collapsed flyout vs expanded
lists), but isCollapsed only resolved after the first paint via auto
rehydration, so a collapsed reload rendered the expanded tree into the 51px
rail and then reflowed — the misplaced/flashing content on refresh.

Adopt zustand's documented SSR pattern: skipHydration on the persist config
(first render keeps the default false, matching SSR HTML) and flush
persist.rehydrate() from a useLayoutEffect so the correct structure commits
in the same pre-paint frame. Removes the old race where onRehydrateStorage
lifted the data-sidebar-collapsed mask before React committed the rail.

* refactor(file-viewer): audit fixes — stale docs, DRY settle-lock, language detection

- rich-markdown-editor: rewrite the now-stale single-surface docstring (no PreviewPanel); extract a
  shared lockSettled() helper used by both the mount and stream-settle paths; guard the settle
  re-seed so it only setContent's when the body actually changed (no redundant doc rebuild)
- detect-language: stop misreading generics (List<String>) as HTML markup; detect Go type/struct
- code-block: export LANGUAGE_OPTIONS + add a test asserting every picker language has a registered
  Prism grammar (prevents picker/highlighter drift)

* refactor(file-viewer): remove dead markdown-preview renderer now superseded by the rich editor

Markdown files route exclusively to RichMarkdownEditor on both the read-only
and editable paths, so PreviewPanel's markdown branch and its Streamdown-based
renderer were unreachable. Delete MarkdownPreview and its renderers, callout/
frontmatter/checkbox machinery, and the now-unused remark/rehype/prism/streamdown
imports; drop the dead toggleMarkdownCheckbox/onCheckboxToggle plumbing in
text-editor. Keep the html/csv/svg/mermaid branches intact.

* refactor(file-viewer): drop dead streamingMode/append path, align naming, cover autosave

The streaming engine only ever runs in 'replace' mode (the only runtime callers
pass it); the 'append' branch of resolveStreamingEditorContent was unreachable.
Remove streamingMode + the StreamingMode type and thread it out of the 6
components that forwarded it — nextContent is now simply the streamed snapshot,
behavior-identical on the live path. Rename for codebase semantics: the boolean
prop streaming -> isStreaming, EditorKeymap -> RichMarkdownKeymap, the highlight
PluginKey KEY -> HIGHLIGHT_PLUGIN_KEY. Add a defensive isEditable guard to the
markdown paste handler (parity with the image handler; read-only must never
mutate). Add a dependency-free useAutosave test suite (debounce, min-display
window, no-data-loss when an edit lands mid-save, error/no-retry, Cmd+S flush,
streaming-disabled lock, unmount flush).

* fix(file-viewer): re-lock round-trip verdict + frontmatter on each stream settle

LoadedRichMarkdownEditor stays mounted across multiple agent edits to the same
file within a chat (previewContextKey is the chat id), but the settle effect only
locked settledRef when it was null — so a second stream into the same instance
kept editability and frontmatter tied to the first settled snapshot. A repeat
edit that is round-trip-unsafe would stay editable, and saves would re-attach the
stale frontmatter. Track wasStreaming and re-derive the verdict + frontmatter on
every stream->settle transition (user edits never re-derive, preserving the
don't-strand-edits rule). Verified red/green in the e2e streaming harness.

* test(file-viewer): lock link href sanitization for dangerous schemes from file content

Greptile flagged a possible javascript: link XSS. Verified TipTap 3.26.1 already
neutralizes javascript:/data:/vbscript: (and mixed-case/whitespace variants) from
file-loaded markdown to an empty href. Add a committed regression test that asserts
this against the real headless editor, so a future TipTap bump can't silently
reintroduce the issue.

* perf(file-viewer): cap the round-trip probe at 24KB and coalesce streaming syncs

@tiptap/markdown's parse is superlinear (~O(n2)) in document size — measured ~170ms
at 11KB, ~875ms at 23KB, multiple seconds past ~35KB — and it runs synchronously at
mount inside the round-trip-safety probe (twice) and the editor's own setContent. The
128KB cap allowed multi-second main-thread freezes; lower it to 24KB so the worst-case
mount stays near a second while still covering the vast majority of real markdown files
(larger files open read-only). Separately, coalesce streaming chunk-syncs to one re-parse
per animation frame so a fast-streaming agent doesn't re-parse the whole accumulating
doc per token. Typing latency was measured to be already excellent (sub-ms median, no
change needed); the only hot cost was the mount parse.

* perf(file-viewer): chunked markdown parsing to remove the O(n2) mount cost

@tiptap/markdown's whole-document setContent(md,'markdown') is superlinear in size,
freezing the main thread at mount for large files (~2.5s at 34KB, ~11s at 65KB) and
forcing a restrictive read-only cap. Parse block-by-block instead: a conservative
blank-line/fence-aware splitter (merges list/quote runs and indented continuations so
ambiguous structures stay atomic; reference-link/footnote/raw-HTML docs fall back to a
whole parse), each block parsed with the editor's own lexer via one reused headless
parser, assembled into a doc. This is linear and byte-identical to the one-shot parse —
measured ~15ms vs multiple seconds at 124KB+ — so the editor mount, streaming sync, and
round-trip probe are all linear, and the editable-size cap goes 24KB -> 256KB (covers
the p99 of real files). Fidelity + idempotency are pinned by unit tests, a 400-document
property/fuzz test, and adversarial edge cases (nested/loose lists, blockquotes, setext,
indented code, lazy continuation, HTML, reference links).

* fix(sidebar): render collapse state from a cookie so SSR matches

The server couldn't read localStorage, so a collapsed user's first paint
rendered the *expanded* tree at 51px — prefetched chat/workflow lists,
pinned-chat pin icons, and loading skeletons all crammed into the rail and
then reflowed once the store hydrated.

Mirror the collapse state into a sidebar_collapsed cookie (the shadcn/ui
sidebar pattern), read it in the workspace server layout, and seed the
sidebar's first render with it: structure is now correct on the server, so
the first paint is the real rail with no skeleton/pin/shift. The store
remains the post-hydration source of truth; the blocking script honors the
cookie for width when localStorage is absent so width and structure agree.

* refactor(sidebar): make the cookie the single source of truth for collapse

Consolidates the collapse machinery onto one source of truth instead of
layering the cookie on top of the legacy localStorage + CSS-mask system:

- Collapse persists only in the sidebar_collapsed cookie; the store seeds
  isCollapsed from it and drops it from localStorage (partialize + merge),
  removing the dual-write and the cross-tab desync it caused.
- Retire the redundant html[data-sidebar-collapsed] attribute + CSS mask now
  that the server emits the correct data-collapsed structure; also delete the
  dead sidebar-collapse-show/-remove/-btn rules.
- Blocking script reads the cookie for collapse (width stays in localStorage)
  and seeds the cookie once from the legacy flag so existing collapsed users
  keep their preference.
- Keep skipHydration + a pre-paint rehydrate for width only — the documented
  zustand SSR pattern, so _hasHydrated is deterministically false during SSR.

Width stays in localStorage; each field now has exactly one home.

* refactor(file-viewer): simplify + cleanup chunked-parse (linear merge, parse-once seed)

From the /simplify + /cleanup passes:
- splitMarkdownBlocks: build continuation runs and join each once instead of
  concatenating onto the growing previous block per group, which was O(n2) for a
  pathological single long loose list (now linear: 208KB loose list splits in ~3ms).
- rich-markdown-editor: seed the editor's initial content via a lazy useState
  initializer instead of useRef(parseMarkdownToDoc(...)), whose argument re-parsed the
  whole document on every render (i.e. every keystroke). Parses exactly once at mount.
- Document that the indent-merge rule is load-bearing for nested fenced code, and
  tighten the verbose inline comment blocks.

* refactor(sidebar): drop orphaned sidebar-collapse-btn class

Its CSS rule was removed with the data-sidebar-collapsed mask; the button's
collapse behavior is fully driven by the React isCollapsed ternary, leaving the
class name pointing at nothing.

* test(file-viewer): consolidate split test files into one per module

Match the dir's one-test-per-module convention: fold the markdown-parse property/fuzz
suite into markdown-parse.test.ts and the editability corpus into round-trip-safety.test.ts
(both already tested the same module from a separate-concern file). No coverage change —
same assertions, fewer files (12 -> 10).

* fix(file-viewer): make all editor controls respect read-only permissions

Every interactive control that calls updateAttributes/dispatches a command mutates
the doc even when read-only (ProseMirror commands run regardless of editable), so
gate them on editor.isEditable:
- bubble menu: the Cmd/Ctrl+K shortcut and shouldShow now bail when not editable, so
  a read-only doc can't open the link bar and setLink into it (Cursor finding).
- code block: the language picker renders as a static label when read-only (its
  onSelect mutates); copy + view-only wrap stay.
- image: no drag-to-reorder (draggable=false, no drag handle) and no resize handle
  when read-only; the image still renders and follows its link.
- links: a plain click now follows the link in read-only (reader) mode, while edit
  mode still requires a modifier so a plain click can place the cursor (Cursor finding).
Verified with new read-only permission e2e tests.

* fix(sidebar): honor collapsed cookie even when localStorage is corrupt

The blocking script read the collapse cookie inside the same try as
JSON.parse(localStorage); invalid persisted JSON fell through to the 248px
fallback and ignored a collapsed cookie, painting an expanded-width rail on
first load. Read collapse from the cookie first and parse the persisted width
in its own try so the two are independent.

* docs(sidebar): convert inline comments to TSDoc

* fix(file-viewer): resolve in-app workspace image URLs in the rich editor

The removed MarkdownPreview rewrote /workspace/{id}/files/{fileId} image src to the
serving endpoint /api/files/view/{fileId}; without it, in-app image URLs 404 in the
rich editor (Cursor finding). Re-add the rewrite as a display-only transform on the
rendered <img src> — the node's stored src attribute keeps the original path so markdown
round-trips unchanged. Absolute/non-workspace URLs pass through. Unit tested.

* fix(files): restore same-page anchor links in the rich markdown editor

Headings rendered by the TipTap editor had no slug ids (the old MarkdownPreview
got them from rehype-slug), so in-document table-of-contents links like
[section](#section) had no targets. Resolve the slug to its heading on click
(GitHub-style, duplicate-disambiguated) and scroll to it, with zero per-keystroke
cost.

* feat(files): render mermaid diagrams in the rich markdown editor

A code block renders as a Mermaid diagram when it is fenced ```mermaid or
auto-detected (an untagged fence whose first line opens with a diagram keyword,
the Linear/GitHub heuristic). Detection is display-only — the node stays an
ordinary code block and the markdown round-trips unchanged.

- Source while the caret is inside the block, diagram on blur; a Show source /
  Show diagram control plus copy, matching the code block's hover chrome.
- Clicking the diagram selects the node (same ring as an image), not flips source.
- Theme-aware (light/dark) via next-themes; the diagram frame shares the code
  block's chrome (one CSS source of truth).
- Extracted MermaidDiagram into a shared module so the editor reuses it without
  pulling preview-panel's heavy deps; rendered SVGs are memoized so toggling the
  source view and back is instant.

Covered by mermaid-diagram unit tests and the editor e2e harness.

* fix(files): harden the markdown editor (CRLF chunking, href allowlist, image escaping)

Final-audit follow-ups:
- splitMarkdownBlocks normalizes CRLF/CR first — a closing fence ending in \r
  no longer fails to match, which had collapsed Windows-authored files with
  fenced code into one block and defeated the linear chunker (perf regression).
- normalizeLinkHref rejects file://, blob:, and other non-network schemes
  (script/data schemes already rejected); network scheme:// (http/ftp/…) and
  bare host:port still pass.
- Image markdown serialization escapes alt/title delimiters and angle-brackets
  a src with spaces/parens, so they round-trip losslessly; linked-image anchors
  open in a new tab (target=_blank).
- Markdown paste routes through the chunker so a large pasted blob can't freeze
  the main thread.

* test(files): cover the code-highlight incremental re-tokenization gate

Export and unit-test changeTouchesCodeBlock: prose-only edits map decorations
(false), edits inside a code block or a setNodeMarkup language change re-tokenize
(true) — the perf-correctness path that keeps highlighting off the keystroke path.

* fix(files): keep relative links relative, navigate in-app links within the SPA

- normalizeLinkHref no longer prefixes `./`/`../` relative paths into `https://./…`
  (they round-trip and resolve correctly).
- Following a same-origin in-app link (e.g. /workspace/…) routes through the
  Next router (same tab) instead of always opening a new tab; modifier-click and
  external URLs still open a new tab.

* fix(files): linked images don't open a tab on a plain click in the editor

The linked-image anchor's native navigation was firing on a plain click in edit
mode (where handleClick intentionally returns false for caret placement). Prevent
the anchor's default so the editor's handleClick — gated on editable/modifier,
matching text links via openOnClick:false — is the sole navigator.

* fix(sidebar): match the collapse cookie value strictly (not a substring)

A substring search for 'sidebar_collapsed=1' also matched 'sidebar_collapsed=10',
desyncing the pre-paint sidebar rail and client store from the strict server read.
Parse the cookie value and compare it to '1' exactly, in both the pre-paint inline
script and readCollapsedCookie. Added a store test.

* fix(sidebar): reconcile migrated-legacy collapse before paint

A user whose collapse lived only in localStorage has no sidebar_collapsed cookie
at SSR (initialCollapsed=false), but the pre-paint script migrates them to a
cookie. The store's persist.rehydrate() is async (flips _hasHydrated after paint),
so the first paint showed expanded labels in the collapsed 51px rail. Reconcile to
the cookie synchronously in a useLayoutEffect (first render still matches the
server, so no hydration mismatch) — no narrow-rail flash.
2026-06-19 18:32:42 -07:00
Theodore Li 208d135dac feat(enrichment): add enrichment details sidebar with cost + provider cascade (#5139)
* feat(enrichment): add enrichment details sidebar with cost + provider cascade

* fix(enrichment): address review — persist detail on cancel/skip, exclude not_run from ran count, refetch on panel open

* fix(enrichment): keep cascade detail sticky on upsert; mark unattempted providers not_run on abort

* fix(enrichment): show Cancelled in details panel for aborted runs
2026-06-19 17:19:13 -04:00
Vikhyath Mondreti 91f9dfdaec improvement(governance): derived access (#5134)
* improvement(governance): org-ws-credential roles clarity

* revert isHosted

* improvement(credentials): code cleanup

* address comments

* make kb cascade delete on user hard delete

* revert env flags

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

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

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

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

* improve copy

* update docs
2026-06-19 12:47:09 -07:00
Waleed 7d46103d09 chore(deps): remove unused dependencies and harden CI supply chain (#5119)
* chore(deps): remove unused dependencies and harden CI supply chain

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

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

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

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

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

* feat(search-modal): add Secrets to the pages list
2026-06-17 14:56:43 -07:00
Siddharth Ganesan 91666b585f feat(scheduled-tasks): migrate jobs agent to scheduled tasks agent (#5090) 2026-06-15 21:00:08 -07:00
Waleed 318cb5e414 chore(deps): bump js-yaml to 4.2.0 and nodemailer to 8.0.9 in apps/sim (#5067) 2026-06-15 13:12:56 -07:00
Theodore Li 43017d72e0 Revert "improvement(auth): layer disposable-email-domains into signup email validation (#5010)" (#5014)
This reverts commit 2c0a10a561.
2026-06-12 20:42:09 -04:00
Theodore LiandClaude Fable 5 2c0a10a561 improvement(auth): layer disposable-email-domains into signup email validation (#5010)
* ci(migrations): fail dev schema push with an actionable error on rename/drop prompt

`drizzle-kit push --force` only suppresses the data-loss confirm, not the
rename-vs-drop disambiguation prompt. That prompt fires whenever a diff both
adds and drops tables/columns at once (e.g. migration 0231 created
sim_trigger_state while dropping the workspace_notification_* tables), and in
CI it crashes with a bare "Interactive prompts require a TTY" stack trace.

Catch that specific failure in the dev push step and emit a GitHub error
annotation explaining the cause and the fix (drop the stale objects on the dev
DB to match schema.ts — the same DROPs the versioned migration already applied
to staging/prod), instead of leaving an opaque trace. Exit status is preserved
either way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* improvement(auth): layer disposable-email-domains into signup email validation

Compose the disposable-email-domains list (exact + wildcard) into better-auth-harmony's validator alongside its bundled Mailchecker list, so signup rejects an email if either flags it. Server-only module to keep the dataset out of the client bundle.

* improvement(auth): defer disposable-domains dataset behind lazy dynamic import

Address review: load the ~120K-entry dataset on first use instead of at module import, so deployments with SIGNUP_EMAIL_VALIDATION_ENABLED off never pay the cost. Add a bare wildcard-base test case.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 18:44:39 -04:00
Vikhyath Mondreti f7b40fe4a4 fix(db-part-1): eliminate pool self-deadlock from nested checkouts inside transactions (#4975)
* fix(db-part-1): eliminate pool self-deadlock from nested checkouts inside transactions

* update docs
2026-06-11 17:29:13 -07:00
Waleed b465a3c6d4 fix(deps): dedupe radix focus-scope/dismissable-layer so in-modal dropdowns open (#4977)
* fix(deps): dedupe radix focus-scope/dismissable-layer so in-modal dropdowns open

@radix-ui/react-dropdown-menu was the only Radix package pinned exactly
(2.1.16), so the v0.7.0 lockfile refresh left its react-menu on
focus-scope@1.1.7 + dismissable-layer@1.1.11 while react-dialog@1.1.16
moved to focus-scope@1.1.9 + dismissable-layer@1.1.12. These packages
coordinate modal/popper interplay through module-scoped singletons
(focusScopesStack, layersWithOutsidePointerEventsDisabled); with two
copies in the bundle, a dialog's focus trap never pauses for a menu
portaled outside it and yanks focus back as the menu opens, so the
menu's dismiss layer unmounts it in the same tick. Symptom: ChipDropdown
menus inside ChipModal (e.g. the credential Add People role dropdown)
never appear in fresh-install/production builds while stale local
installs with a single shared copy keep working.

Bump dropdown-menu to 2.1.17, whose react-menu pins the same internals
batch as dialog 1.1.16 — every coordination package (dismissable-layer,
focus-scope, focus-guards, portal, popper, presence) now resolves to a
single version tree-wide.

* fix(deps): use caret range for dropdown-menu so radix internals track the same batch

Address review: the exact pin was the original divergence mechanism, and
the rest of the radix popper/dialog family floats on caret ranges that
jump to the same internals batch together on lockfile refreshes.
2026-06-11 13:55:22 -07:00
Will ChenandClaude Opus 4.8 bc55fc3b50 improvement(docs): builder-first IA reorganization of the English docs (#4896)
* docs: reorganize into topic/ontology IA with a builder-first rewrite

Restructure the English docs from internal product categories into a
topic-based information architecture, and rewrite the conceptual pages
to install a mental model first rather than enumerate features.

Structure & navigation
- Reorder the sidebar to follow how someone builds: Get Started ->
  Workflows -> Tables -> Files -> Knowledge Bases -> Logs ->
  Building agents -> Mothership -> Workspaces -> Platform -> Reference.
- Demote the generated blocks/tools/triggers catalogs to a Reference
  section at the bottom.
- Break up the monolithic execution/ folder into deployment/ and
  logs-debugging/; collapse connections/* and variables/* into single
  pages under workflows/.
- Rename capabilities/ to building-agents/; relabel the integration
  catalog as "Integrations". Remove deprecated copilot and form
  deployment. Redirects added in next.config.ts for every moved URL.

Conceptual rewrites
- Workflows core (index, how-it-runs, data-flow, connections,
  variables): one mental model, one running example, terser prose.
- New building-agents overview distinguishes an agent (a workflow you
  build) from an Agent block (one reasoning step), plus a "choosing
  what to use" guide.
- Concept-trim passes on Knowledge Base, Tables, Blocks, Triggers
  overviews; new task pages for KB, Tables, and Files.
- New code-verified Alerts page.

Infrastructure
- pageType frontmatter (concept/guide/reference) + badge render.
- WorkflowPreview / OutputBundle components to embed real, app-styled
  workflow diagrams (adds framer-motion + reactflow to apps/docs).

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

* feat(docs): spec-driven BlockPreview for block reference heroes

Replace the static screenshot hero on each block reference page with a
<BlockPreview> that renders the block exactly as the builder canvas shows
it — header icon, sub-block rows, and branch/error handles — from a
hand-authored display spec. Static and non-interactive (no ReactFlow), so
it can't be panned or dragged, and self-updating to edit.

- block-display-specs.ts: one editable spec per block (rows, branches, handles)
- block-preview.tsx: static scaled card renderer with decorative handles
- block-icons.tsx: brand glyphs for the core block types; icons.tsx adds WaitIcon
- 14 block + 3 trigger pages swapped from <Image> to <BlockPreview>

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

* fix(docs): correct stale navigation and removed-feature references

Audited the docs against the product changelog (GitHub releases / staging
git history) for content that misleads readers — features that moved, were
renamed, or removed — rather than cosmetic drift. Fixes:

- Skills: no longer a Settings tab. It was promoted to its own workspace
  page (#4354), so "Settings → Skills under the Tools section" sent readers
  to a tab that no longer exists. (skills/index.mdx)
- Env vars: the workspace tab is "Secrets", not "Environment Variables"
  (credentials→secrets rename, #4364). (quick-reference/index.mdx)
- Mothership FAQ pointed to "Settings → Credentials" for integration
  connections; integrations moved to their own page and there is no
  Credentials tab. (mothership/tasks.mdx)
- Vision block was retired (#4684); a tip still named it. Reworded to
  "an Agent using a vision-capable model". (files/passing-files.mdx)
- Getting-started FAQ told new users to "use the Copilot feature" to build
  in natural language — that surface is Mothership. (getting-started)
- Removed the dead "Mod+Y → Go to templates" shortcut; the templates
  gallery was removed (#4354). (keyboard-shortcuts)

Note: MCP "tools" (Settings → Tools, for consuming) and MCP "servers"
(Settings → System, for exposing) are distinct surfaces — both doc
references are correct and were intentionally left as-is.

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

* fix(docs): repair broken /docs-prefixed enterprise links

The enterprise overview linked to /docs/enterprise/* (access-control, sso,
whitelabeling, audit-logs, data-retention, data-drains), but the docs site
is served at root — those 6 links 404'd. Now root-relative /enterprise/*.

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

* fix(docs): refresh stale workflow-preview example blocks

The /workflows diagram blocks are hand-authored (separate from the
spec-driven BlockPreview heroes) and had drifted from the real UI:
- Agent color purple #6f3dfa -> green #33C482 (the var(--brand) rebrand)
- Model gpt-4o -> claude-sonnet-4-6 (current default)
- "Prompt" row -> "Messages" (the actual agent sub-block)
- Start color #34B5FF -> #2FB3FF (real starter bgColor)

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

* fix(docs): align BlockPreview input/output handles to the card edge

The header (input/output) handles are positioned relative to the card and
used a -16px offset, so they floated 8px past the edge. Row/error handles
are -16px relative to a row that's already inset 8px by content padding, so
they sit correctly. Header handles are now -8px, so every handle sticks out
the same 8px and hugs the block edge.

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

* docs(blocks): rewrite Agent reference to match the current block

The page documented the old UI (System/User Prompt, no Files or Skills, Memory
taught as a separate block — contradicting its own FAQ). Rewritten to the real
sub-blocks (Messages, Model, Files, Tools, Skills, Memory, Response Format) in
the builder voice of the workflows exemplars: oriented opening, agent vs
Agent-block callout, outputs table, a live WorkflowPreview example, FAQ kept and
corrected (tool control "Force", not "Required"). pageType: reference.

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

* docs(blocks): rewrite API reference to match the current block

Tightened to the builder voice and the real config (URL, Method, Query Params,
Headers, Body + Advanced timeout/retries/backoff). Dropped the off-topic
"Dynamic URL Construction" / "Response Validation" sections (those are
Function-block techniques, not API config). Outputs table, FAQ kept. The example
is now a live WorkflowPreview (new API_FETCH_WORKFLOW in examples.ts, exported
via the barrel). pageType: reference.

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

* docs(blocks): rewrite Condition reference to match the current block

Tightened to the builder voice: oriented opening (branches on boolean
expressions, no model call, vs Router), the real branch model (if / else if /
else, checked top to bottom), connection-tag expression examples, an error-path
callout, outputs table, and a live branching WorkflowPreview example
(CONDITION_ROUTE_WORKFLOW). FAQ kept. pageType: reference.

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

* docs(blocks): restore Best Practices + multi-example workflows on Condition

Recalibration: reference pages keep genuine substance (Best Practices, every
distinct example), cutting only redundancy and verbose register. Restores the
Best Practices section and turns the three use cases into three rendered
WorkflowPreview examples (route by priority, moderate content, branch
onboarding). Adds CONDITION_MODERATE_WORKFLOW and CONDITION_ONBOARD_WORKFLOW.

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

* docs(blocks): restore Best Practices on Agent reference

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

* docs(blocks): restore Best Practices on API reference

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

* docs(blocks): rewrite Function reference to match the current block

Fixed the verbose register and dropped the duplicated outputs section + the
stale Python screenshot/TODO, while keeping the real substance: JS vs Python
(local vs E2B sandbox), the large-inputs sim.files/sim.values helpers, the
worked loyalty-score example, and Best Practices. The use cases are now two
rendered WorkflowPreview examples (reshape an API response, validate input).
Adds FUNCTION_RESHAPE_WORKFLOW and FUNCTION_VALIDATE_WORKFLOW. pageType: reference.

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

* docs(blocks): rewrite Router reference to match the current block

Cleaned the register, generalized the drifting model list, and folded the
Router-vs-Condition guidance into a callout. Kept the substance (routes as
output ports, NO_MATCH error path, all seven outputs, Best Practices, FAQ). The
three same-shape use cases collapse to one rendered triage WorkflowPreview
(ROUTER_TRIAGE_WORKFLOW), which the prose notes stands for the pattern.
pageType: reference.

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

* docs(blocks): restore the classify and lead-qual examples on Router

I wrongly folded two distinct Router scenarios into a note. Restored all three
as their own rendered WorkflowPreview examples: triage a support ticket,
classify feedback (to child workflows), qualify a lead (sales vs self-serve).
Adds ROUTER_CLASSIFY_WORKFLOW and ROUTER_LEAD_WORKFLOW. (Also exports
RESPONSE_API_WORKFLOW for the next page.)

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

* docs(blocks): rewrite Response reference to match the current block

Cleaned the register and broadened "Variable References" to connection tags
(any output, not just workflow variables). Kept the substance: exit-point
semantics, Builder/Editor mode, status codes, headers, the parallel-branch
warning, Best Practices, FAQ. All three use cases are now rendered
WorkflowPreview examples (API endpoint, webhook ack, status-per-branch). Adds
RESPONSE_API/WEBHOOK/ERROR_WORKFLOW. pageType: reference.

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

* docs(blocks): rewrite Variables reference to match the current block

Cleaned the register, corrected the outputs (each assignment is also exposed as
<variables.name>, not "no outputs"), and kept the substance: assignments
reference earlier outputs and current values, global <variable.name> access,
Best Practices, FAQ. Two use cases now render as WorkflowPreview examples (count
retries, hold config). Adds VARIABLES_RETRY_WORKFLOW and VARIABLES_CONFIG_WORKFLOW.
pageType: reference.

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

* docs(blocks): rewrite Wait reference to match the current block

Corrected a real staleness: the block now has an Async mode that suspends the
run for minutes/hours/days (not a hard 10-minute cap), plus a resumeAt output.
Documents Wait Amount / Unit / Async, the sync-vs-async distinction, all three
outputs, Best Practices, and updated FAQ. Two rendered WorkflowPreview examples
(space out API calls, delayed follow-up). Adds WAIT_RATELIMIT_WORKFLOW and
WAIT_FOLLOWUP_WORKFLOW. pageType: reference.

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

* docs(blocks): polish Credential reference (frontmatter, fold redundant tabs)

The page was already accurate to the block (Select/List operations, the outputs
tabs, the wiring steps). Light touch only: added description + pageType, made the
header consistent, and folded the two identical Gmail/Slack "how to wire" tabs
into one line. Examples stay as labeled flows + the List/ForEach screenshot,
since they use integration blocks and a Loop the WorkflowPreview can't render.

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

* docs(blocks): render the shared-credential example + icon fallback for integrations

Addressing the gap: WorkflowPreview block nodes now fall back to the integration
icon map, so diagrams can show Gmail/Drive/Slack/etc. with their real glyphs, not
just core blocks. Renders the Credential "share one account across blocks" example
as a WorkflowPreview (CREDENTIAL_SHARE_WORKFLOW). The multi-account and
List+ForEach examples stay as labeled flows + screenshot (the latter uses a Loop
container the preview can't render). Also exports EVALUATOR_GATE_WORKFLOW for the
next page.

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

* docs(blocks): rewrite Evaluator reference to match the current block

Cleaned the register, generalized the drifting model list, and documented the
per-metric outputs (<evaluator.metricname>), which the page omitted. Kept the
substance (metrics with name/description/range, structured-output guarantee,
Best Practices, FAQ). The quality-gate example renders as a WorkflowPreview;
the same shape covers the parallel-variations and support-QC patterns, noted in
prose. pageType: reference.

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

* docs(blocks): render the Credential route-by-logic example too

The icon fallback unblocked it: the "route to a different account by logic"
example now renders as a WorkflowPreview (CREDENTIAL_ROUTE_WORKFLOW), a Condition
selecting a production vs staging credential. The List + ForEach example stays a
screenshot because it nests blocks in a Loop container the flat WorkflowPreview
can't represent.

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

* docs(blocks): render Guardrails examples + light accuracy pass

Kept the full substance (four validation types, PII entity/language detail,
the PII screenshot and video, outputs, Best Practices, FAQ). Light fixes:
frontmatter, and generalized the drifting model names (GPT-4o / Claude 3.7) to
"a strong reasoning model" with the current default. The three use cases now
render as WorkflowPreview examples (validate JSON, check grounding, block PII).
Adds GUARDRAILS_JSON/HALLUCINATION/PII_WORKFLOW. pageType: reference.

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

* docs(blocks): render Human-in-the-Loop examples + frontmatter

Kept all the substance (Display Data, Notification, Resume Form, the Approval
Methods and API Execute Behavior tabs, outputs, the paused/resume example).
Added frontmatter and rendered the use cases as WorkflowPreview examples
(approve before publish, two-stage approval, verify extracted data); Quality
Control folds into the approval note as the same approve-then-act shape. Adds
HITL_APPROVAL/MULTISTAGE/VALIDATE_WORKFLOW. pageType: reference.

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

* docs(blocks): render Webhook examples + frontmatter

The page was already accurate (Webhook URL/Payload/Signing Secret/Headers, the
automatic-headers table, HMAC details, outputs, POST-only callout, FAQ). Added
frontmatter and rendered the two use cases as WorkflowPreview examples (notify a
service, fire on a check). Adds WEBHOOK_NOTIFY_WORKFLOW and
WEBHOOK_TRIGGER_WORKFLOW. pageType: reference.

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

* docs(blocks): add example + pageType to Workflow block reference

The page was already accurate and well-structured (Configure It, outputs,
deployment-status badge, execution notes, FAQ). Added pageType: reference and a
rendered WorkflowPreview example showing a parent calling the child workflow
enrich-lead and reading its result. Adds WORKFLOW_CALL_WORKFLOW.

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

* docs(blocks): container rendering for Loop/Parallel + render the Loop example

Adds subflow/container support to WorkflowPreview, modeled on the app's
subflow-node.tsx: a solid-bordered box with a header (icon + name), an internal
"Start" pill whose handle feeds the first nested block, and target/source
handles at the vertical center. PreviewBlock gains size/parentId; edges gain an
optional sourceHandle; nodes render nested children via React Flow parentNode.
Renders the Loop reference's ForEach example (LOOP_WORKFLOW) and keeps the four
loop-type sections + inside/outside referencing + caps. pageType: reference.

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

* docs(blocks): fix the Loop container's Start-pill connector

The Start pill -> first-block edge wasn't rendering: it was a React Flow
parent->child edge (unreliable), and the opaque container body hid it. Nested
blocks now render as absolute-positioned top-level nodes (container below at
zIndex 0, blocks above at zIndex 1), so the connector is an ordinary edge, and
the container body is see-through so it's visible.

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

* docs(blocks): render the Parallel example + frontmatter (last core block)

Reuses the container rendering for the Parallel reference. Kept all substance
(count/collection types, inside/outside referencing, batch size of 20, instance
isolation, the Parallel-vs-Loop table, Best Practices, FAQ). Added frontmatter
and a rendered container WorkflowPreview (PARALLEL_WORKFLOW: distribute tasks,
call concurrently, aggregate <parallel.results>); the two use cases stay as
labeled flows. Adds PARALLEL_WORKFLOW. pageType: reference.

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

* docs(blocks): prose glow-up for Guardrails to match the agent/condition voice

Rewrote the listy register (**Use Cases:** / **How It Works:** / **Configuration:**
scaffolding, "Use this when you need to..." filler) into the plain builder voice,
matching the depth of the Agent/Condition/Function rewrites. Kept every
validation type, option, range, the full PII entity/region list, the screenshot
and video, the outputs table, the rendered examples, Best Practices, and FAQ.

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

* docs(blocks): prose glow-up for Loop to match the agent/condition voice

Rewrote into the plain builder voice and cut the filler: dropped the "Use this
when you need to..." lines and the ASCII "Example: Iteration 1, 2, 3" pseudo-code,
and folded the duplicated Inputs/Outputs tabs into Configuration + Referencing
sections. Kept all four loop types with their screenshots, the inside/outside
reference rules, the 1,000-iteration cap, sequential-vs-parallel guidance, the
rendered example, and FAQ.

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

* docs(blocks): prose glow-up for Parallel to match the agent/condition voice

Same treatment as Loop: plain builder voice, dropped the ASCII pseudo-code and
the duplicated Inputs/Outputs tabs, folded the verbose Advanced Features into
tight Configuration + Referencing sections. Kept both types with screenshots,
the batch-size-of-20 cap, instance isolation, large-result indexing, the
Parallel-vs-Loop table, the rendered example, and FAQ.

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

* docs(blocks): prose glow-up for Human-in-the-Loop

Tightened the register: folded the pause sentence into the intro, made the
section headers consistent (Configuration, Outputs), converted the bold-list
Block Outputs into a table, condensed the Notification channel bullets to a
line, and renamed the second "Example" so it no longer collides with the
rendered Examples. Kept all the substance — Display Data / Notification / Resume
Form, the Approval Methods and API Execute Behavior tabs, the portal video, and FAQ.

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

* docs(blocks): re-enrich Loop prose (fuller, explanatory — not terse)

The first glow-up overcorrected into terse fragments. Restored proper
docs-quality prose at the Agent/Condition level: each loop type now explains
what it does, when to use it, and the relevant reference; Configuration,
Referencing, nesting, and Best Practices give context and the "why," not just
bullets. Same substance, readable depth.

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

* docs(blocks): re-balance Parallel prose to the Agent/Condition register

Calibrated to the level signed off on elsewhere: each concept explained in a
couple of clear sentences with a concrete detail — informative, not terse, not
padded. Kept both types with screenshots, batch-size cap, isolation, large-result
indexing, the Parallel-vs-Loop table, the rendered example, and FAQ.

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

* docs(blocks): restore the Notification channel detail on HITL

The glow-up over-compressed: it flattened the five notification channels (each
with what they do) into one sentence. Restored them as a list in plain voice —
tightening register shouldn't drop genuinely useful reference detail.

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

* docs(blocks): builder-voice polish on the Credential intro

Light touch only — the page was already well-structured and explanatory, so just
led the intro with what the block does (and bolded the name) to match the other
references. No content changed elsewhere.

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

* docs(triggers): rewrite Start trigger in the builder voice

Tightened the register, swapped the <code>&lt;&gt;</code> noise for backticks,
added pageType + an outputs table, and kept all substance: Input Format types,
chat-only outputs (input/conversationId/files), the editor/API/chat tabs, and
best practices.

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

* docs(triggers): rewrite Schedule trigger in the builder voice

Plain voice and clean markdown (dropped the raw <ul>/<div> lists). Kept all
substance: simple intervals, cron examples, timezone, deploy-tied activation,
the 100-failure auto-disable, and FAQ. Added pageType.

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

* docs(triggers): refocus Webhook trigger on the generic (native) trigger

Rewrote in the builder voice and separated out the integration content: the
page now documents the generic Webhook trigger (URL, Input Format, auth, custom
response, outputs, dedup/rate-limit/deploy/no-auto-disable). The "trigger mode
for service blocks" section is reduced to a short pointer + the demo video, and
the long supported-services catalog and vague use-case bullets are dropped in
favor of the Triggers index. Fixed the title (Webhook) and added pageType.

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

* docs(triggers): builder-voice glow-up for RSS

Light pass: added pageType + description, tightened the intro, and presented the
output fields as an <rss.*> outputs table. Kept the polling config, use cases,
the published-after-save callout, and the FAQ (poll cadence, dedup, 25-item cap,
auto-disable, Atom support).

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

* docs(triggers): rewrite Table trigger off the auto-generated card

Replaced the BlockInfoCard/'provides 1 trigger' auto-gen format with a real
builder-voice page: a spec-driven BlockPreview hero (added a 'table' spec),
plain-language Configuration (table, event type, watch columns, include
headers), and a full <table.*> outputs table. pageType: reference.

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

* docs(triggers): frame the index around native triggers + separate the catalog

Reframed "generic" as native (no connected account) and promoted RSS and Table
into the native set alongside Start/Schedule/Webhook — cards, comparison table,
and integration paragraph updated to match. In the sidebar, grouped the five
native triggers under a "Native triggers" header and divided the ~44 service
triggers under "Integration triggers" (nav-only — no files moved, URLs stable;
the move to integrations/ is a later, separate change).

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

* docs: promote Core Blocks + Core Triggers into the Workflows area

Restructured the Documentation sidebar (meta-only — no files moved, URLs stable):
after Deployment, the 16 core block pages now live under a "Core Blocks" section
and the 5 native trigger pages under "Core Triggers", instead of buried in the
bottom Reference catalog. Removed the now-redundant blocks tree from Reference,
and retitled the Reference triggers tree "Integration triggers" so it holds just
the service catalog (the native ones are promoted up top).

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

* docs: merge block/trigger overviews into the Workflows overview; Core accordions

Restructured the sidebar and overview hub (meta + content only, no integration
files moved):

- Folded the /blocks and /triggers overview pages into /workflows: the overview
  now carries the core-block catalog (do work / direct flow / shape run), the
  Integrations-and-triggers families framing, the native + integration trigger
  framing, the trigger comparison, manual-run priority, and email-polling groups.
  Deleted blocks/index.mdx and triggers/index.mdx as redundant.
- Promoted the 16 core blocks into a "Core Blocks" folder accordion and the
  native triggers into a "Core Triggers" accordion, both under Workflows after
  Deployment. Integration triggers stay inside Core Triggers under a labeled
  divider, temporary until they move to integrations/<service> (tabs) later.
- Repointed every /blocks and /triggers index link to the /workflows#blocks and
  /workflows#triggers sections.

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

* docs: split integration triggers into their own Reference accordion

Core Triggers is now the 5 native triggers only. Moved the 43 service triggers
out of triggers/ into a new integration-triggers/ folder, surfaced as an
"Integration triggers" accordion under Reference (an accordion must be its own
folder in Fumadocs). In Workflows, Core Triggers now sits before Core Blocks.
URLs: /triggers/<service> -> /integration-triggers/<service> (native /triggers/*
unchanged); the integrations/<service> tabbed-page migration remains the later step.

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

* docs(workflows): trim the overview back to an introduction

It had drifted from a concept intro into a catalog. Kept the spine (the four
parts with their previews, how-it-runs, workflows-in-context) and compressed the
merged-in material: the full 16-block enumeration becomes a three-kind taxonomy
with examples, the trigger section a short native/integration framing. Cut the
anxious in-between — manual-run trigger priority, the niche email-polling-groups
feature (belongs on the Gmail/Outlook trigger pages), the redundant block-def
line, the Start-outputs callout half, the connections video, and the catalog-y
FAQ items. Dropped the unused Video import.

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

* docs: relocate email-polling + trigger-priority out of the overview

Moved the two bits cut from the workflows overview to durable, generator-safe
homes: email-polling groups -> the Integrations (connecting accounts) page;
manual-run trigger priority -> the Start trigger page. Also added 'table' to the
generator's HANDWRITTEN_TRIGGER_DOCS / SKIP_TRIGGER_PROVIDERS so the hand-written
Table trigger page is no longer overwritten by generate-docs.

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

* feat(docs-gen): emit per-service integration pages (actions + Trigger section)

Rewrites the generator to output one page per service under integrations/
instead of split tools/ + triggers/. Block pass writes the service's actions;
trigger pass appends a '## Triggers' section (badged) to the same page, or writes
a standalone page for trigger-only services. Meta is written after both passes;
hand-written integration pages are preserved; docsUrl repointed to /integrations.

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

* feat(docs): unify tools + triggers into per-service /integrations pages

Encodes the ontology "everything is a block; some blocks are triggers." The
generator now emits one page per service under integrations/ — the service's
Actions plus, when it has one, a Triggers section on the same page — replacing
the split tools/<service> + triggers/<service>. No "Tools" terminology.

- generate-docs.ts: output to integrations/, merge trigger sections into each
  service page (standalone for trigger-only services), Actions heading, table
  block now generated, docsUrl -> /integrations, hand-written pages preserved.
- Nuked tools/ (213) and the interim integration-triggers/ (43); moved the
  custom-tools guide to building-agents/; knowledge/memory/file/table links and
  meta repointed to /integrations.
- Sidebar: integrations catalog now under Reference (was tools); removed the
  Workspaces integrations entry and the integration-triggers tree.
- block-icons: wait uses lucide Clock (the generated icons.tsx no longer carries
  a hand-added WaitIcon). Landing integrations data regenerated.

No redirects (fresh start). Native Core Blocks/Core Triggers unchanged.

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

* fix(docs): recover the hand-written manual-content intros on integration pages

The tools->integrations relocation generated fresh pages, so the generator never
saw the old tools/<service>.mdx to preserve its {/* MANUAL-CONTENT */} sections —
198 curated intros (AgentMail, etc.) were dropped. Reseeded each integrations
page from the pre-move tools page in git, re-ran the generator (which now merges
the manual intro into the new Actions/Triggers format), and repointed /tools/
links inside the recovered prose.

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

* docs(scripts): rewrite the generator README for the integrations model

Brings scripts/README.md current: integration pages are derived from the
apps/sim block/tool/trigger registry (canonical-sources map), the golden rule
not to hand-edit generated pages, the MANUAL-CONTENT escape hatch, which pages
are hand-written/skipped, and the icons.tsx-overwrite gotcha.

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

* docs: regenerate integration docs from staging-synced apps/sim

After merging staging, regenerated so the integration pages reflect current
source: correct block colors/configs (e.g. Gmail #FFFFFF), the new integrations
(sendblue, millionverifier, neverbounce, zerobounce), and staging's icon set.
Pages for integrations staging hid are removed; manual-content intros preserved.

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

* fix(docs-gen): don't let stale-doc cleanup delete hand-written integration pages

Staging's cleanupStaleToolDocs removes any integrations/*.mdx that isn't a visible
tools block — it only guarded `index`, so it deleted the hand-written
google/atlassian service-account pages. Now guards all HANDWRITTEN_INTEGRATION_DOCS.
Restored the two pages, and repointed /integrations/file links to /files (staging
hides the file block, so it has no integration page).

Note: staging recategorized a2a/mysql/postgresql tools -> 'blocks' (and hid file),
so they correctly drop out of the integration catalog and are currently
undocumented — an IA decision to revisit.

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

* fix(docs-gen): stop cleanup/writer filter mismatch from eating manual content

Comprehensive-review findings, all generator-consistency bugs:
- cleanup used staging's isIntegrationBlock while the writer kept the legacy
  filter, so integrations/{knowledge,memory,table}.mdx were deleted then
  regenerated without their manual intros every run. Both now honor a shared
  NATIVE_RESOURCE_BLOCK_TYPES set; intros reseeded.
- Trigger-only services (imap, circleback; category 'triggers') were likewise
  deleted each run; the canonical set now includes visible trigger-category
  blocks, the standalone writer preserves manual content, and their intros are
  reseeded.
- Mapped jsm -> jira_service_management, so JSM triggers merge into the JSM
  integration page instead of an orphan jsm.mdx (removed).
- Repointed lingering bare /tools links to /integrations; added missing
  pageType to integrations/index and building-agents/custom-tools.
Double-regen is now churn-free (idempotent) with all manual content intact.

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

* fix(docs): recover staging's enriched Table doc + never drop manual content

The merge resolution deleted staging's relocated blocks/table.mdx, which carried
substantial enrichment our integrations/table.mdx (reseeded from the older
tools/ version) lacked: Creating Tables (column types/constraints), Filter
Operators, Combining Filters, Sort Specification, Built-in Columns, Limits, and
Notes. Recomposed integrations/table.mdx with that content — Creating Tables
inside the intro manual section, the reference tail in a notes manual section.

Generator fix uncovered en route: a manual section whose insertion anchor is
missing in the generated markdown (e.g. notes with no "## Notes" heading) was
silently dropped on regen. Unplaceable sections now append at the end instead —
manual content is never lost. Verified idempotent across double regeneration.

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

* docs(workspaces): de-philosophize the fundamentals prose

Rewrote in the plain register of the workflows overview: 'draws the boundary
for access' / 'Nothing crosses the boundary' / 'follow the same edge' become
direct statements (only members can access it; a workflow in one workspace
cannot read a table in another). '## The boundary' is now '## Access and
isolation'. All substance kept: every resource type, permission levels,
personal/organization/grandfathered kinds, deployments callout, VISUAL markers.

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

* fix(docs): restore #blocks and #triggers anchors on the workflows overview

The editorial trim renamed '## Blocks' -> '## Kinds of blocks' and
'## Triggers' -> '## How a workflow starts', silently breaking the ten
/workflows#blocks and /workflows#triggers anchor links pointed there when the
old index pages were folded in. Pinned the original ids with explicit heading
anchors. Found by the comparative prose review.

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

* docs: restore the genuinely useful reference bits the rewrite dropped

From the comparative prose review, restored in guidance register (no spec
dumps): temperature tiers on Agent (low/middle/high with ranges), loop/parallel
iteration references in the variables syntax-at-a-glance table, and a short
"Test it" section on the Webhook trigger (curl + check the run in Logs). The
fourth flagged loss (tag-resolver mechanics on connections) turned out to be
already covered — name normalization, case-sensitive paths, missing-output
behavior, and value formatting are all on the page; only the internal resolver
precedence chain was dropped, deliberately.

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

* docs(blocks): rework the Agent intro — encyclopedia register

Replaced the flat opening with a denser, factual one (no metaphor): what the
block does, and its centrality stated as fact — 'Most workflows are built
around one or more Agent blocks.' The agent-vs-Agent-block disambiguation moves
from an info callout into a second paragraph on the block's role in building
agents. Dropped the now-unused Callout import.

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

* docs(integrations): add the HubSpot setup guide for the Marketplace listing

Addresses HubSpot Marketplace review item A1: a public, HubSpot-specific setup
guide following their template — what the app does, install + connect through
the current flow (sidebar Integrations page -> HubSpot -> Add to Sim -> connect
dialog -> HubSpot OAuth), with real screenshots of each step and a placeholder
for the scope-approval shot; configure in a workflow (one-click skills/templates
+ the HubSpot block + trigger mode), use, disconnect (with data consequences),
uninstall from the HubSpot side, troubleshooting. Capability wording is by CRM
object rather than scope enumeration, so it stays accurate after the A2 scope
trim. Lives at /integrations/hubspot-setup, guarded as hand-written,
cross-linked from the HubSpot reference page's intro.

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

* docs(integrations): rewrite the Integrations guide for the sidebar flow

Integrations moved out of Settings to a top-level sidebar page. Rewrote the
guide to the current journey: the Integrations page (Connected/Featured/search),
service pages with one-click skills and templates, + Add to Sim -> connect
dialog (display name + permissions) -> provider OAuth. Replaced the four
Settings-era screenshots with current captures (connect dialog illustrated via
HubSpot); block-side screenshots (account selector, manual credential ID) kept;
one VISUAL marker for the connection detail view pending a fresh capture.
Members/roles, credential-ID, reconnect/disconnect, email polling, and FAQ
substance unchanged apart from navigation.

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

* docs: move Building agents directly after Workflows in the sidebar

The agent-building journey follows straight from workflows (blocks, triggers,
deployment) rather than after the tour of every resource type. Tables/Files/
Knowledge Bases/Logs now follow it. Meta-only reorder.

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

* docs: fill the visual slots coverable by existing components

Six VISUAL markers filled with no new captures needed:
- building-agents overview: rendered the minimal lead-scoring agent
  (Start -> Agent with tool chips -> Response, Agent highlighted) as a
  WorkflowPreview (BUILD_AGENT_WORKFLOW)
- files guide: the read -> summarize -> write chain as a WorkflowPreview
  (FILE_SUMMARY_WORKFLOW)
- tables guide: the query -> classify -> write-back roundtrip as a
  WorkflowPreview (TABLE_ROUNDTRIP_WORKFLOW)
- choosing guide: the six-kind comparison grid as a markdown table
- knowledgebase guide: the Knowledge block's output as an OutputBundle
- workspace fundamentals: removed a duplicate nesting-diagram marker

42 -> 39 VISUAL markers remaining (screenshots + designed diagrams).

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

* docs(components): run-inspector OutputBundle + lightbox with block inspector

Two visual-component upgrades, both mirroring the real app:

- OutputBundle is now a miniature of the run inspector: a Logs column (block
  rows with icon chips and durations, source selected) beside the Output panel's
  typed tree — keys with the app's type-badge semantics (string green, number
  blue, object gray, array purple, boolean orange), chevrons, indent guides,
  primitive values. Styling lifted from the terminal's structured-output.
  Dropped the "Read one value by name" footer (the prose teaches the tag).
  The three usages (data-flow, tables, knowledgebase) get real typed trees;
  data-flow's stale purple/gpt-4o example corrected en route.

- WorkflowPreview gains a lightbox + read-only block inspector: clicking a
  block (or the expand control) opens a 92vw/86vh overlay with zoom and pan,
  and a right-hand inspector panel showing the selected block's full
  configuration — canvas rows truncate, the inspector doesn't. Fields render as
  app-style controls (dropdown/textarea/input by heuristic) with dashed
  dividers, tool chips, and a Connections footer computed from the edges.
  Selection rings without dimming (new selectedBlock option in workflow-data).
  Esc/backdrop closes; body scroll locks while open.

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

* docs: regenerate after staging merge — AppConfig joins integrations/

Staging's new AWS AppConfig integration (#4928) generated its docs into the old
tools/ layout; re-homed to integrations/appconfig.mdx (Actions heading, meta
entry) via the generator. tools/ stays deleted.

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

* docs: redirect the retired tools/ and trigger URLs to integrations/

Revises the earlier fresh-start call: /tools/* are ~200 live, indexed URLs
referenced by deployed app versions' docsLink fields and marketplace listings,
so dropping them cold would 404 from the live product. next.config now 308s:
- /tools -> /integrations, /tools/:slug -> /integrations/:slug
  (custom-tools -> building-agents/custom-tools first)
- old /triggers/<service> -> /integrations/<service>, enumerated so the native
  trigger pages keep resolving; provider-slug mappings for jsm and the
  hyphenated Google/Microsoft slugs
- /blocks and /triggers index URLs -> the workflows overview anchors
Verified every class + native passthroughs against the dev server. Spec updated.

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

* docs(getting-started): rewrite — current UI, cut the post-tutorial padding

The last old-guard page. Accuracy: Agent config now uses Messages (System/User
message) instead of the removed System Prompt/User Prompt fields, the default
model instead of GPT-4o, the banned 'no-code' phrasing is gone, the deploy card
points at /deployment, and frontmatter gets description + pageType. Weight: cut
the 'What You've Built' checklist, the 'Key Concepts You Learned' re-teach
section, the duplicate 'Resources' links, the Start-block hand-holding, and ten
dead icon imports; tightened every step preamble. 203 -> 113 lines with the
full 5-step tutorial, videos, and FAQ intact. (Videos still show the old UI
until the re-recording pass.)

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

* docs: de-fluff the Tier-1 heavy pages (logging, mcp, passing-files, permissions)

From the exhaustive fluff audit, keeping all substance:
- logging: merged the duplicated Console/Logs-page structure, snapshot concept
  stated once instead of three times, cut the generic Best Practices, trivial
  tab walkthrough condensed. Frontmatter added.
- mcp: intro + "What is MCP?" generic bullets folded into two sentences, cut
  the Common Use Cases catalog and the verify-your-config Troubleshooting
  checklists, merged the twice-stated Refresh behavior, security kept as one
  real warning.
- passing-files: marketing opener replaced with a factual lead, fixed the stale
  retired-Vision-block reference (now Agent with a vision model), dropped the
  FAQ item that restated the block catalog verbatim.
- permissions: heading-restating intro replaced with the two-layer model, cut
  the three "Perfect for: stakeholders..." persona lines and the generic Best
  Practices section, dropped the FAQ restating the limits table.
- connectors: audit over-flagged it — the categorized support matrix, API-key
  table, and config examples are genuine reference; frontmatter only.

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

* docs: tier-2 fluff trims (costs, enterprise, mailer, skills)

Conservative sweep from the audit, unambiguous cuts only: the costs CYA opener
and formula restatement, the enterprise marketing intro (now a functional
summary), mailer's restated convenience line and chat-upload comparison, and
skills' third restatement of progressive disclosure. Audit flags screened out
as misfires: mothership/tasks (immediate-vs-scheduled are two facts, not a
duplicate), self-hosting telemetry (real sizing data), and the recently
approved credential/HITL/workflow-block/trigger pages.

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

* docs(skills): update to the Skills tab on the Integrations page + document import

Skills moved again — they now live on the Integrations page's Skills tab in the
workspace sidebar (the doc said "Open the Skills page"). Updated the create flow
(+ Add to Sim -> Add Skill dialog) with fresh screenshots of the tab and both
dialog tabs, and documented the previously-missing Import flow: upload a .md
with YAML frontmatter or a .zip containing SKILL.md, fetch from a GitHub URL, or
paste SKILL.md content (verified against the import route/component; name 64 /
description 1024 limits verified against the contract). Noted the curated
skills suggested on integration pages, cross-linked the Skills tab from the
Integrations guide, and refreshed the location FAQ. Mechanics (progressive
disclosure, load_skill, agent-block attachment) unchanged and still accurate.

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

* docs(building-agents): render the lead-scorer running example on choosing

The page narrated its running example through six sections without ever showing
it. Authored LEAD_SCORER_WORKFLOW (Start -> Enrich workflow-as-tool -> Function
reshape -> Agent with Search/Send Email/CRM tool chips -> Google Sheets append)
and rendered it after the intro, with highlightBlock re-renders in the three
sections that map to a node (deterministic block -> the Sheets append, agent
tool -> the Agent, workflow-as-tool -> Enrich) — the same pattern as the
workflows overview.

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

* docs(tables): rewrite workflow columns around the real lead-scoring example

Rebuilt the page on the ai_startup_customers screenshots instead of captioning
them onto the old hypothetical: one running example throughout — Company Domain
fills domain, Company Info reads it into employee_count/description, Lead Score
Enrichment writes lead_score/priority/score_reasoning. Every section now
describes the actual UI: the grid with group headers, per-row run buttons, and
the 21-running toolbar; the Configure workflow panel (picker, column inputs,
output selection, Auto-run, Run after); the Company Info input/output mapping;
Not found cells explained where the screenshot shows them; the cascade section
describes the example itself. All placeholder markers on the page resolved.

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

* docs: regenerate after staging merge — Slack trigger update + file block re-visible

Staging's mothership v0.2 (#4923) expanded the Slack trigger payload
(interactivity, slash commands: event_type, command, action_id/value/actions,
response_url, trigger_id, callback_id, ...) — regenerated so it lands on the
unified integrations/slack page; the old-layout triggers/slack.mdx from
staging's generator was dropped in the merge. The file block is visible again
upstream, so integrations/file.mdx is back in the catalog.

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

* docs(tables): playbook prose pass on workflow columns + restore File block links

Workflow columns, against the docs-writing playbook: killed the banned
'Term — desc' bullets in the Configure list (term + verb form), restored the one
universal analog (spreadsheet macro), fixed the clipped 'On,/Off,' fragments,
replaced an invented <start.companyDomain> tag with the verified description,
and thinned em-dashes to four page-wide with no clustering. Also repointed
[File] block mentions back to /integrations/file now that the page exists again
(FileV5 is visible upstream); the Files-store links stay on /files.

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

* docs(tables): per-row execution inspection on workflow columns

Two new captures: the cell menu (View execution, Re-run cell, row actions) and
the Log Details trace for a single row's run. New 'Inspecting a row's run'
section ties cell values to real, traceable runs; corrected the re-run guidance
now that Re-run cell exists (the page previously said Run all rows was the only
way to retry).

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

* docs(workflows): drop the confusing 'order by hand' sentence

'You never set the order by hand' read wrong (wiring connections is setting it
by hand), and the replacement was over-explanation. The first sentence already
carries it: Sim works out the order from the connections.

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

* docs(workflows): fix the over-claim about independent blocks

'Two blocks that don't depend on each other run at the same time' is wrong —
independent blocks at different depths run at different times. Concurrency
follows from readiness, not independence: blocks whose dependencies have all
finished run together. Reworded to say that, tied to the image's two agents.

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

* docs(workflows): accuracy audit of how-it-runs against the executor

Verified every claim on the page against apps/sim/executor. One claim was
materially false: "a failed block stops its own path but leaves independent
paths running" — in the engine, an unhandled block failure sets the error flag
and stops scheduling entirely (in-flight blocks finish, nothing new starts);
only a connected error port routes the failure and keeps the run alive. Now
says that. Two imprecisions tightened: a join waits for every feeder *that is
going to run* (deactivated-branch feeders don't hold it up, per the
edge-manager cascade), and Loop also repeats while a condition holds. Confirmed
accurate: per-block readiness scheduling (readyQueue + race, not layers),
branch-skip cascade and empty tags, the 25-hop call-chain cap.

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

* docs(logs): real captures on the overview + prose matched to the UI

The logs-debugging overview had six visual placeholders and no visuals. Three
real captures placed: the workspace Logs page as the hero (rows with status,
credits, trigger, duration), Log Details' Trace tab at the blocks section (the
CRM sync run's spans, with a one-line read of where the time went), and the
editor's live run console at the input/output section. Prose corrected to what
the UI shows: cost is in credits, failed runs are badged Error (dropped the
five-state enum the list doesn't display), and the Trace tab is named. The
row-anatomy marker is covered by the hero; the two designed-diagram markers
(debug-loop flowchart, failed-vs-success comparison) remain.

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

* docs(workflows): one reference syntax, named sources — untangle variables vs connection tags

An exhaustive sweep of "connection tag" found the docs asserting both that a
workflow variable is a connection tag (response.mdx used it as the umbrella for
all angle-bracket references) and that it isn't (variables.mdx). Ruled the
narrow definition canonical — a connection tag reads a block's output; the name
follows the connection — and restructured around the real model:

- variables.mdx: new "One syntax, named sources" section states that everything
  in angle brackets is one mechanism whose first segment names the source, with
  the load-bearing fact stated plainly: `variable` is literal, a connection tag
  starts with the block's own name. The syntax table drops the redundant
  dot-notation row, gets one row per source, and is ordered by resolution
  precedence with the order explained beneath it (absorbing the old Name
  conflicts section). The credentials pointer folds into the env-var section;
  trimmed the "never appears in outputs" overclaim.
- response.mdx: no longer calls a workflow variable a connection tag.
- connections.mdx: the owner page closes the loop — same syntax also reads
  variables and loop/parallel context; a connection tag is the block-output case.

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

* docs(workflows): verify the reference model against the resolver; fix one imprecision

Checked every claim in the new 'One syntax, named sources' section against
apps/sim/executor/variables: resolver chain order is Loop -> Parallel ->
WorkflowVariables -> Env -> Block (matches the table); 'variable'/'loop'/
'parallel' are literal prefixes (REFERENCE.PREFIX); block names normalize via
toLowerCase + strip spaces; an unmatched reference is genuinely left in place
(resolver returns undefined -> the replacer emits the raw match). One claim
tightened: {{KEY}} is a different syntax and can never collide with
angle-bracket references, so the precedence sentence now scopes collisions to
the angle-bracket sources with a concrete example (a block named 'variable').

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

* docs(building-agents): workflow-as-tool is agent-decided, not the Workflow block

The choosing page defined workflow-as-tool as the Workflow block (path-decided),
contradicting its own name and the comparison table's premise. Verified against
the product: workflow_executor is an agent tool — you pick the workflow in the
Agent block's tool list, the model decides when to call it and supplies the
inputMapping (user-or-llm), inputs arrive at the child's Start trigger.

Rewritten agent-first: the section defines it as a workflow handed to an agent
as one callable tool, the lead scorer gains a Deep Enrich workflow tool chip on
the agent (diagram updated), and the deterministic Workflow block becomes the
explicit contrast in a callout — same child workflow, the difference is who
decides, mirroring the block/agent-tool contrast. Table row corrected to
"The agent"; the summary paragraph follows.

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

* docs: theme-aware previews + enrichments vs workflow groups split

Light-mode support for every preview component (WorkflowPreview canvas, nodes,
containers, edges, lightbox, BlockPreview, OutputBundle, BlockInspector): a
wp-scope token block in the docs global stylesheet whose values mirror the OG
repository's globals.css in both modes (surfaces, borders, --workflow-edge,
text tiers, the --badge-* type-badge palette). Every hardcoded hex swapped to a
--wp-* var; brand colors, selection blue, and error red stay literal.

tables/workflow-columns: separated the two group kinds per the contract's
workflowGroupType enum ('manual' | 'enrichment'). New "Two kinds of groups"
section opens with the + New column menu capture (Enrichments above the types,
Workflow below); Enrichments documented from the code-defined registry (company
domain, company info, email verification, phone number, work email) including
the provider-cascade behavior that produces Not found cells; the Company Info
panel capture is now correctly labeled as an enrichment config; workflow groups
keep the Configure workflow panel. Shared machinery generalized under "How
groups run"; the cascade section names which stage is which kind; the two
portrait screenshots render smaller.

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

* docs(tables): don't enumerate the enrichment catalog; don't assert a group's kind

Two corrections on workflow columns: the prose no longer lists the enrichment
catalog (growable, not procedurally tracked — it now describes the category and
points at the Enrichments panel; the provider-cascade/Not-found explanation
stays, it's behavior not catalog), and the page no longer asserts which kind
the example's Company Domain / Company Info groups are (Company Info may be a
user-built workflow, not the built-in). The input/output bindings capture moved
to "How groups run" as the kind-agnostic illustration; only Lead Score — whose
panel shows the workflow picker — is named as a workflow group.

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

* docs(components): per-branch source handles — conditions and routers finally branch

WorkflowPreview's node only ever had one header source handle, so every
condition/router example fanned both edges out of a single point and never
showed the if/else rows the real canvas (and the BlockPreview hero specs)
render. PreviewBlock now supports `branches` (each rendered as a row with its
own right-edge source handle, id `branch-<id>`) and `showError` (red error
handle), mirroring the executor's per-branch condition-true/condition-false and
router-<route> handle model. A block with branches emits from them, not the
header.

Every affected example rewired (13 workflows): the three condition examples,
status-per-branch, credential routing, and the webhook-trigger check route
their edges through branch-if/branch-else with the expression on the If row and
an explicit else; the three router examples list their actual routes as branch
rows (Sales/Support/Billing, Product/Bug report, Enterprise/Self-serve); the
terminal gates (variables retry, evaluator gate, the three guardrails gates)
show dangling if/else branch rows like the canvas does.

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

* docs(components): inspector shows branch rows

Moving condition expressions from rows into branches emptied the lightbox
inspector for condition/router blocks — it only mapped rows to fields. Branches
now map too: each branch renders as a field (If with its expression as code,
else as an empty control, router routes by name).

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

* docs(components): branch handle ids match the app's workflow representation

Verified against the source after the branch-handles work: the canvas emits
condition-${cond.id} handles per condition row (workflow-block.tsx) and Router
V2 uses router-${routeId} port handles, and edges carry those ids as
sourceHandle — the docs' invented branch- prefix was a gratuitous divergence
that the planned fromWorkflowState() adapter would have had to translate. The
node now uses the authored branch id as the handle id directly, and every
example authors ids in the app's own scheme (condition-if/condition-else,
router-<route>), so example edges now match real workflow edges verbatim.

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

* docs: agent skills mint /integrations/ docs links and describe the new output

The add-integration/add-block/validate-integration skills — what Claude Code
follows when integrations land on staging — still taught the old layout:
docsLink templates pointing at docs.sim.ai/tools/{service} and 'generates
tools/{service}.mdx'. Updated so that once this PR merges, the instructions on
staging produce the new way by themselves: /integrations/ docsLinks, the
per-service page description, and the don't-hand-edit/manual-content pointer.

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

* docs(workflows): execution semantics, not simultaneity

The concurrency section drifted into 'run at the same time' framing across two
accuracy passes — but the semantics are non-blocking execution: a block starts
the moment its dependencies finish and waits on nothing else. Section retitled
'Blocks run as soon as they can', the rule stated in two plain sentences, the
duplicated pre-image example narration gone (the post-image caption carries it).

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

* docs(workflows): errors are execution semantics — own section on how-it-runs

Failure behavior was buried inside 'Watching a run' (the live-UI section). Now
a first-class 'When a block fails' section in the execution story: an error
fails the run (in-flight blocks finish, nothing new starts) unless the block's
error port is connected, in which case the run follows the error path.

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

* docs: data-driven additions from the platform-metrics read

Three targeted edits from the sim-internals analysis, each carrying an inline
{/* why */} provenance comment so future editorial passes know the data behind
it:

- workflows/how-it-runs gains "How long a run can take" — run timeouts are the
  only hard-error class provable at scale (2,415 five-minute timeouts in 14
  days); limits verified in lib/core/execution-limits/types.ts (5 min free /
  50 min paid sync, 90 min async, env-overridable).
- getting-started gains an "if the run doesn't go green" callout at the Test
  step — the largest funnel drop is created-workflow -> first-successful-run
  (92% -> 49%), and this is the stall point.
- function/api Best Practices: the existing error-path bullets get a guard
  comment (<1% of deployed workflows connect an error port — under-adopted,
  not under-needed) instead of duplicate bullets.
- visuals manifest: capture priority reordered by integration adoption
  (Sheets, Gmail, Telegram, WhatsApp, ...).

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

* docs: regenerate after staging merge (integration validation batch + Gong tools)

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

* docs: rename Building agents -> Agents; URLs match the settled IA

The section's pages now live where the sidebar says they do:
building-agents/ -> agents/, and the stray top-level /mcp and /skills fold in
as /agents/mcp and /agents/skills (they were always part of the agents story —
the URLs predated the IA settling). Sidebar section header is now "Agents",
link labels updated, and every old URL 308s: /building-agents(/*) -> /agents(/*),
/mcp, /skills, plus the existing capabilities/ and tools/custom-tools redirect
destinations retargeted. Verified: all five new pages render and every old-URL
class redirects correctly.

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

* docs(workflows): connections gets its video, an FAQ, and accurate output examples

The reorg dropped two things from the old tags page that belonged on the
connections reference: the connections.mp4 walkthrough (restored after the
intro) and the FAQ (rebuilt in the robust JSX form — resolver order, name
normalization, env-var syntax pointer, didn't-run behavior, array indexing,
Function-block formatting; answers aligned with the since-verified resolver
facts, including unmatched-references-left-in-place).

Editorial/accuracy pass on the output-shape tabs while in there: stale gpt-4o
and gpt-5 examples now claude-sonnet-4-6, the Agent tokens shape corrected to
the verified { input, output, total } (the page contradicted blocks/agent), and
the dubious cost: [] line dropped — the example now matches the real run
inspector.

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

* docs: regenerate after staging merge — sim trigger, enrichment + logs blocks, re-shown DB integrations

Staging's #4941 added the Sim workspace-event trigger (hand-written page adopted
into Core Triggers), the Enrichment and Logs blocks (category 'blocks' — added
to NATIVE_RESOURCE_BLOCK_TYPES so they live in the integrations catalog like
table/knowledge/memory), and re-categorized mysql/postgresql/sftp/smtp/ssh back
to visible tools (their pages return to the catalog). Generator sets merged as
the union of both sides (sim in HANDWRITTEN_TRIGGER_DOCS + SKIP_TRIGGER_PROVIDERS,
enrichment in the icon allowlist).

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

* docs: regenerate after staging merge (CodePipeline); suppress sim trigger from catalog

The native Sim workspace-event trigger is documented at triggers/sim — the
block writer no longer emits an integrations page for it (skip + canonical-set
exclusion). CodePipeline (#4945) lands in the catalog in the Actions format.

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

* docs(blocks): cross-link the Memory block from the Agent memory section

Final loss audit found the old page's pointer from built-in agent memory to the
standalone Memory block had been dropped; one line restores it.

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

* docs: URLs now mirror the sidebar — sections own their pages

Every page lives at a path matching its meta.json section, done now while none
of these URLs are publicly live (the last free window before merge):

- Workflows owns its accordions: /blocks/* -> /workflows/blocks/*,
  /triggers/{start,schedule,webhook,rss,table,sim} -> /workflows/triggers/*,
  /deployment/* -> /workflows/deployment/*
- Mothership owns Mailer: /mailer -> /mothership/mailer
- Workspaces & Access folds into Platform, sequenced concept-first with the
  reference tail last: /platform/{workspaces,organization,permissions,
  credentials,costs}, then platform/self-hosting/*, platform/enterprise/*
  (from /workspaces/fundamentals+organization, /permissions/roles-and-
  permissions, /credentials, /costs, /self-hosting/*, /enterprise/*)

All internal links swept (0 broken in a full-tree resolver sweep), root
meta.json repointed, and every previously-live URL 308s to its new home —
including retargeted destinations of existing redirects so chains stay
single-hop (verified: /execution/chat reaches /workflows/deployment/chat in
one hop), and the native-trigger rule ordered after the enumerated
integration-trigger redirects so /triggers/gmail still reaches
/integrations/gmail. Production build passes.

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

* docs: untrack .plans/ (local agent planning files)

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

* docs(preview): tool chips use the EMCN ChipTag chrome

The canvas previews' tool chips were ad-hoc (5px radius, header surface, plain
border). The app's canonical chip chrome is the ChipTag family: 20px tall,
rounded-md, px-1, gap-1.5, --surface-5 light / --surface-4 dark with an inset
--border-1 ring and --text-body label. Mirrored those values into --wp-chip-*
tokens (both modes) and restyled the chip; the integration's brand-color icon
square stays, sized to the chip.

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

* chore(blocks): one-time shift of all docsLinks to the new docs URLs

Block definitions are the patterns coding agents copy from, so redirects alone
leave new blocks minting dead conventions. Every docs.sim.ai link in apps/sim
now points at the final URL scheme: /tools/<slug> -> /integrations/<slug>
(433 links), /blocks/<core> -> /workflows/blocks/<core> (knowledge/enrichment/
logs -> /integrations/*), native /triggers/* -> /workflows/triggers/*,
/mcp -> /agents/mcp, /self-hosting + /enterprise -> /platform/*, plus the
llms.txt listings and the blocks.test.ts assertions.

Verified every rewritten target against the docs tree: all resolve except ten
hidden blocks (vision, spotify, thinking, tts...) and a2a whose links were
already dead pre-reorg — no regressions introduced.

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

* docs: ignore .plans/ (local agent planning files)

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

* docs(files): align every File-block claim with the shipped file_v5 block

Accuracy audit against apps/sim/blocks/blocks/file.ts (FileV5Block, the visible
block) and tools/file/*:

- The block has FIVE operations, not four — Get Content was missing entirely.
- Read outputs file objects only; the page claimed it also returned extracted
  text. Text comes from Get Content (contents, per file) or Fetch
  (combinedContent) — table, prose, and the Fetch callout corrected.
- Functions CAN read files: sim.files.readText/readBase64 exist in the sandbox
  (isolated-vm-worker.cjs), so "doesn't reach into workspace storage" is gone;
  the section now teaches Get Content text or sim.files on the file object.
- Workspace file IDs are wf_<shortId> (workspace-file-manager.ts:511), not f_.
- Stale "such as Claude or GPT-4o" vision parenthetical dropped.
- "File block reference" card pointed at /files (the section overview); now
  /integrations/file.
- FILE_SUMMARY example agent consumed <file.combinedContent>, which Read never
  produces — now binds the file object to the Files input.
- passing-files.mdx: combinedContent scoped to Fetch, contents documented.

Verified intact: Write's numeric-suffix collision behavior, Fetch's auth
headers, Append-by-name, and the file-object shape.

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

* docs: keyboard-shortcuts audited against the command registry; cut legacy workspace detail

Every binding verified against commands-utils.ts (the global registry),
workflow.tsx, and table-grid.tsx. Three fixes: tables Mod+A (select all rows)
doesn't exist — the real bindings are Shift+Space (select row, was misworded
as a toggle) and the undocumented Mod+Space (select column); the global
Mod+Shift+A row conflated two commands — add-agent (Mod+Shift+A) and
add-workflow (Mod+Shift+P) are separate. All 29 other documented shortcuts
confirmed accurate, including tables clipboard (native copy/cut/paste events)
and Mod+Y redo (tables only — correctly absent from the workflow editor
section).

Also drops the grandfathered_shared workspace paragraph — internal billing
taxonomy, not something a reader can act on.

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

* docs: apply Theodore's accuracy feedback

- getting-started: workflow creation is the + button next to Workflows in the
  sidebar (no "New Workflow" button exists); Exa/Linkup no longer need
  user-supplied API keys on hosted Sim (apiKey is hideWhenHosted in the Exa
  block) — step and FAQ updated.
- workflows overview: chat and API are entry points of the Start trigger, not
  separate triggers — the "swap in a chat/API trigger" sentence now matches
  triggers/start's own model.
- variables: names cannot contain periods — the resolver reads everything
  after the first dot as a path into the value (executor/variables/resolvers/
  workflow.ts splits on dots) — constraint now stated where name normalization
  is taught.

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

* docs: Python sandbox package list (verified) + agent/agents cross-linking

Function block: the Python callout's "common packages like matplotlib" becomes
the actual package list, grouped by use. Sources verified 2026-06-10 and cited
in an inline provenance comment: E2B's code-interpreter template requirements
(the base Sim's mothership-shell template builds from) plus Sim's three pip
additions (awscli/yq/csvkit, per the copilot repo's template.ts via
sim-internals). Versions omitted so the list doesn't rot on routine bumps.

Agent surfaces deduplicated by direction: blocks/agent's Tools section now
links custom tools and MCP and points at the Agents concept page for tool
sourcing; agents/index drops its duplicated Auto/Force/None enumeration in
favor of the block reference, which owns config mechanics.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:39:57 -07:00
Theodore Li 1ff445ae80 feat(codepipeline): add AWS CodePipeline integration with tools and block (#4945)
* feat(codepipeline): add AWS CodePipeline integration with tools and block

* fix(codepipeline): address review feedback on input coercion and error statuses

* chore(hooks): restore use-inline-rename onSave type accidentally swept into previous commit
2026-06-10 14:51:24 -04:00
Vikhyath MondretiandCursor 6abcf82db2 feat(workflows): sim trigger, logs v2 block, toolbar renaming (#4941)
* feat(workflows): sim trigger, logs v2 block, toolbar renaming

* fix(review): bound rule queries, canonical logs params, watched-workflow SQL scoping

Code-review fixes: read the canonical workflowIds param in logs_v2 (the
serializer deletes the source pair ids), aggregate failure-rate in the DB
and switch rule windows to the indexed startedAt column, clamp rule config
to the legacy contract bounds, push no_activity watch scoping into SQL
before the LIMIT, fix the generated sim icon-map key, normalize docs
wording, and drop dead exports.

Co-authored-by: Cursor <cursoragent@cursor.com>

* address comments

* fix(review): integer rule rounding, success-gated workflow labels, display module hygiene

Second-pass review fixes: round integer rule fields so fractional input
never reaches SQL LIMIT, gate workflow-name readiness on a successful
non-placeholder load in both editor and preview (errored loads mislabeled
valid workflows as deleted), lazily read the variables store in preview
rows, move the filter-field JSON preview into the shared display module
and unexport its single-consumer helpers, and align >= boundary copy
(failure rate, error count, cooldown window) with implementation.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: sync lockfile after staging merge

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(workspace-events): keyset-paginate the no_activity subscription scan

A fixed LIMIT 500 with no ORDER BY silently starved subscriptions beyond
the cap once the global count exceeded it. The poll now pages by webhook
id so every subscription is visited each cycle; pagination bounds memory,
not total work.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(workspace-events): keyset-paginate the watched-workflow scan

The 500-row LIMIT silently and deterministically excluded high-id
workflows from no_activity coverage in watch-everything subscriptions on
large workspaces. The scan now pages by workflow id, mirroring the
subscription scan; per-workflow checks move into a helper so the
pagination loop stays flat.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(workspace-events): skip no_activity subscriptions on the execution-completion path

no_activity is poller-owned and can never fire from a completed execution,
but it passed into the rule branch and cost a pointless cooldown point-read
per subscription on the hottest path. Early-continue alongside the
workflow_deployed guard.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(sim-trigger): note failure-based alert conditions evaluate on failed runs

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(blocks): recategorize Data Enrichment as a core block

It's a Sim-native capability (registry enrichments over a managed provider
cascade, like Search), not a third-party integration. Moves it to Core
Blocks in the toolbar, out of the integrations catalog, and relocates its
docs page to blocks/ with the icon-map allowlist keeping the docs card icon.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(blocks): recategorize MySQL, PostgreSQL, SFTP, SMTP, SSH as integrations

External-system connectors with host/credential auth belong under
Integrations, not Core Blocks — consistent with MongoDB, Redis,
ClickHouse, and the other datastore integrations. They already carried
integrationType and /tools docsLinks; the regenerated docs pages turn
those previously-dangling links into real pages, and the blocks join the
integrations catalog and icon maps.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-10 11:04:12 -07:00
Emir Karabeg 192f77ba02 improvement(emcn): consolidate chip chrome, enforce ChipModalField, paint real chrome in loading fallbacks (#4935)
* improvement(emcn): consolidate chip chrome, enforce ChipModalField, paint real chrome in loading fallbacks

- Move chip chrome single-source to chip/chip-chrome.ts (surface, typography, and new content tokens); delete chip-input/chip-field-chrome.ts
- Rework Chip variants: implicit default replaces ghost, filled reserved for chip fields/triggers and removed from Chip's public API; add ChipChevron
- Migrate every labeled modal body field to ChipModalField across knowledge, settings, tables, files, deploy, sidebar, and ee modals
- Replace skeleton loading.tsx files with ResourceChromeFallback that paints the page's real header, actions, search/filter/sort chips, and column headers
- Rename resource-options-bar to resource-options; simplify resource.tsx and resource-header
- Delete legacy Breadcrumb, Callout, and FormField components
- Add shared connector-config-fields (knowledge) and ee InfoNote; add useTagUsageQuery; make useInlineRename save async with isSaving
- Update AGENTS.md/CLAUDE.md and emcn/styling rules (with .cursor/.agents mirrors)

* fix(rename): make inline rename await mutateAsync so isSaving disables the field; recover edits on failure

- useInlineRename: catch onSave rejection — log, restore the original name,
  keep the edit session open, and re-arm doneRef (mirrors useItemRename)
- switch rename call sites (files, tables, table header, knowledge base,
  document) from mutate() to mutateAsync() so isSaving spans the request
- table-grid column rename stays fire-and-forget by design (optimistic local
  update + undo entry)

* fix(rename): type onSave as void | Promise<unknown> so fire-and-forget call sites pass the build

table-grid's optimistic column rename is a block-bodied callback returning
void, which is only assignable when the declared return type is literally
void — 'undefined | Promise<unknown>' rejected it and failed the Next.js
type check in CI.
2026-06-09 20:04:45 -07:00
+1 39418f9615 improvement(mothership): v0.2 (#4923)
* CONTRACTS

* updates

* Prompt caching

* Fix regression

* updates to prompt caching

* Prompt caching trace

* VFS updates

* Changelog and plan

* VFS update

* Improvement/mothership (#4775)

* ff

* System role in cache

* Fixes

* Add dynamic user info

* Improve tool search

* improvement(platform): workspace UI/UX overhaul + integrations catalog

Rework the workspace around the AI-workspace model: a Mothership home, a
top-level Skills route, connected-credential and integration-detail pages,
and a polished sidebar/settings surface. Replace the notifications store
with a unified toast system (provider-level dismiss/pause, countdown ring).

Integrations & catalog:
- Add a BlockMeta layer (tags + catalog templates) scoped to catalog-visible
  integrations; every catalog integration carries >=7 grounded templates.
- Rework the taxonomy: each block declares category tools|blocks|triggers.
  3rd-party services are 'tools'; first-party primitives (postgres, mysql,
  knowledge, file, search, stt/tts, image/video generators, thinking, etc.)
  are 'blocks'. Versioned blocks follow the upgrade paradigm (old hidden,
  latest in toolbar/docs).
- Generate integrations.json + tool docs canonically from block configs.

Architecture & cleanup:
- Consolidate block data extraction behind a single latest-version strategy
  (getCanonicalBlocksByCategory; version-consistent getBlockMeta).
- Unify version-suffix handling in @sim/utils/string (stripVersionSuffix /
  isVersionedType, with tests); registry, generate-docs, tools/utils, and
  integrations all route through it.
- Repair latent broken barrels, remove dead code, fix BlockMeta-related type
  errors and 5 broken docs links.

Behavior-preserving for block execution and the toolbar's tool/block listing.

* refactor(platform): remove forms, templates, and creators features

Remove three standalone features and their supporting code:
- Forms: form-deployment pages, API routes, execution path, and docs.
- Templates: the template gallery (landing + workspace) and template APIs.
- Creators: creator-profile routes and contracts.

Add a super-user permissions module (lib/permissions/super-user) and an
organizations API contract; update the audit/db/testing packages, billing,
and the session/theme providers accordingly.

* New doc setup

* Fixes

* Fixes

* Logs tools

* test(workflows): update archiveWorkflow update count after forms removal

The forms feature was removed, dropping the form-table update from
archiveWorkflow. Update the stale assertion from 8 to 7 tx.update calls.

* Add finalization on error

* Fixes

* change dev CI to bun run db:push

* Updates

Please enter the commit message for your changes. Lines starting

* Contrat update

* improvement(nested): subagents

* updates

* upgrade

* improvement(knowledge): polish tag filter dropdowns (#4816)

* improvement(logs): object storage backed tracespans (#4787)

* improvement(logs): obj storage backed tracespans

* fix storage write context

* fix tests

* address comments

* address comments

* chore(db): remove migration 0219 to regenerate after staging merge

Drops the 0219_robust_shard SQL, its snapshot, and the journal entry so the
trace-spans/cost schema migration can be regenerated on top of the latest
staging migration chain (avoids a number collision with staging's migrations).

Co-authored-by: Cursor <cursoragent@cursor.com>

* improvement(billing): accurate per-member usage via shared ledger helper

Per-member/per-user usage in the org-member routes now adds the usage_log
ledger to the currentPeriodCost baseline (which is no longer incremented),
via a shared getOrgMemberLedgerByUser helper to avoid repeating the
subscription→period→ledger lookup across the admin and member-facing routes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* regen migrations

* update migration

* address comments

* more code cleanup

* incorrect type cast

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* improvement(providers): harden OpenAI-compatible providers + add tests (#4796)

* improvement(providers): harden OpenAI-compatible providers + add tests

* fix(vllm): let tool-loop errors propagate instead of returning silent partial success

* fix(litellm): force tool_choice 'none' on final structured-output call

The deferred final call used tool_choice 'auto', so the model could emit
another tool_calls round instead of the structured answer, leaving content
stale. Use 'none' (matching vLLM/Fireworks) on both the streaming and
non-streaming final calls so the model must return the structured response.

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

* fix(providers/ollama): drop tools from post-tool streaming call

Ollama ignores tool_choice (not in its supported fields), so vLLM/Fireworks'
tool_choice:'none' guard is a no-op here. Omit tools from the final streaming
payload instead so the summarization turn can't emit dropped tool calls.

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

* fix(litellm): spread payload into deferred final call so reasoning_effort carries over

The non-streaming deferred finalPayload hand-picked fields and dropped
reasoning_effort (and any future payload field), diverging from the streaming
path which spreads ...payload. Spread payload here too for consistency.

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

* chore(providers/ollama): restore enrichment TSDoc block

Keeps parity with sibling Chat Completions providers (cerebras/mistral/xai).

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

* docs(fireworks): restore TSDoc on utils helpers

Restore the TSDoc blocks on supportsNativeStructuredOutputs,
createReadableStreamFromOpenAIStream, and checkForForcedToolUsage —
TSDoc is the codebase documentation standard and should not have been
stripped.

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

* chore(litellm): remove inline rationale comments (codebase uses TSDoc)

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

* chore(providers/ollama): drop orphaned enrichment TSDoc

The block documented a function that now lives in trace-enrichment.ts, so it
documents nothing in this file.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* chore(copilot): deprecate mcp server (#4797)

* chore(copilot): deprecate mcp

* update error codes

* deprecate copilot api v1 route

* feat(integrations): hosted API keys for Findymail, Prospeo, and Wiza (#4777)

* feat(integrations): hosted API keys for Findymail, Prospeo, and Wiza

Add hosted-key support across all credit-consuming Findymail, Prospeo, and Wiza operations so Sim provides the key when a workspace has not brought its own. Register the three BYOK providers, consolidate Wiza's two-step reveal into a single polling wiza_individual_reveal op, and hide the API key field on hosted Sim for hosted operations.

* fix(integrations): harden Wiza reveal polling, soften enrichment getCost guards

Address Greptile + Cursor Bugbot review on #4777: return explicit failures from the Wiza individual_reveal poller instead of throwing (thrown errors were swallowed into a false queued success), short-circuit when the initial reveal is already terminal, tolerate transient 5xx/429 during polling, and return 0 (not throw) from Findymail getCost when the contacts/employees array is absent.

* chore(integrations): biome formatting after wiza merge resolution

* fix(wiza): type isTerminalReveal param structurally for next build typecheck

* feat(enrichments): add Findymail, Prospeo, Wiza to work-email waterfall

* feat(enrichments): add Wiza + Prospeo phone reveal to phone-number waterfall

* feat(enrichments): opportunistic identifiers + LinkedIn URL input across work-email & phone cascades

* fix(tables): reduce column header chevron size and fix sidebar shadow bleed (#4800)

* feat(slack): add install + privacy section to integration landing page (#4799)

* feat(slack): add install + privacy section to integration landing page

Adds a hand-authored, slug-keyed landing-content module (separate from the generated integrations.json so it survives regeneration) and renders an install walkthrough + privacy-policy link on integration pages when present. Also refreshes generated docs (data-enrichment entry, icon mappings, tool mdx).

* fix(landing): render privacy section independently, align CTA analytics label

* docs(landing): clarify the Slack install button is behind sign-in

* refactor(landing): bake integration landing content into generated json via docs-gen

Moves landing content (install walkthrough + privacy) out of a render-time augment and into the generation pipeline: generate-docs reads the pure-data content map and writes landingContent into integrations.json, so the page reads a single source (integration.landingContent). Canonical types live in integrations/data/types.ts.

* improvement(enrichments): align enrichments sidebar with design system (#4801)

* improvement(enrichments): align enrichments sidebar with design system

* fix(enrichments): consistent close button pattern and fix url link hover

* fix(misc): upgrade path change for new better-auth version, billing issue for workflow block agent usage (#4803)

* fix(misc): upgrade path change for new better-auth version, double-billing for workflow block agent usage

* fail loudly if stripe sub id missing

* fix(copilot): seq migration (#4804)

* chore(db): drop redundant idx_webhook_on_workflow_id_block_id index (#4809)

Removed because (workflow_id, block_id) is a left-prefix of idx_webhook_on_workflow_id_block_id_updated_at_desc, which fully covers it. The dropped index was non-unique and enforced no constraint.

* perf(copilot): read chat transcripts from copilot_messages (R+1 cutover) (#4808)

* perf(copilot): read chat transcripts from copilot_messages, not JSONB

Flip user-facing chat reads from the legacy copilot_chats.messages JSONB
array (5.7GB, 99% TOAST) to the normalized copilot_messages table via a
new loadCopilotChatMessages helper ordered by seq NULLS LAST, created_at,
id — the verified canonical order. Both chat-detail getters
(getAccessibleCopilotChat, getAccessibleCopilotChatWithMessages) now drop
the messages column from their metadata select (no more whole-array
detoast on every load) and assemble the transcript from the table after
authorization. This cascades to the copilot + mothership GET endpoints
and to resolveOrCreateChat's conversationHistory (the LLM payload).

The normalize/effective-transcript pipeline is source-agnostic
(copilot_messages.content == a JSONB array element), so transcripts are
byte-identical. Dual-write and the JSONB column stay in place as the
internal-logic source and fallback; removing JSONB writes is a later step.

Prod integrity verified before cutover: 0 messages missing, 0 NULL-seq,
0 dup keys/seq, 0 orphans, order-parity vs JSONB = 0 mismatches.

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

* test(copilot): cover auth-deny on a found row skips the messages query

Address PR review: exercise the `if (!authorized) return null` contract —
when the chat row exists but authorization fails, the getter returns null
and never issues the copilot_messages read.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(tables): right-align run/stop in embedded toolbar; workflow cells format like normal cells (#4806)

* fix(tables): right-align run/stop in the embedded table toolbar

Add a right-aligned `trailing` slot to ResourceOptionsBar and move the embedded
mothership table's run/stop control into it, so Filter + Sort stay left-aligned
and run/stop sits opposite on the right. No-op for the search-bearing consumers
(logs, resource list), which don't pass `trailing`.

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

* fix(tables): workflow-output cells format values like normal cells

Workflow-output columns short-circuited in resolveCellRender and rendered their
value as plain text, so a sim-resource URL / external URL / JSON / date produced
by a workflow never got the chip, favicon link, or typed formatting a normal
cell gets. Factor value formatting into a shared `resolveValueKind` helper used
by both the workflow-value branch and the plain-cell branch; the workflow branch
keeps the typewriter reveal for plain streaming text via a `typewriter` flag.

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

* fix(tables): detect resource/URL links on workflow output regardless of column type

Workflow output columns default to `json` (columnTypeForLeaf), so routing their
values through the type-based formatter (a) gated chip/URL promotion behind
`column.type === 'string'` — a URL produced by a json-typed output never became
a chip — and (b) JSON.stringify'd plain string values, adding quotes and losing
the typewriter reveal. Detect links (sim-resource chip / favicon URL) on the
value string directly for workflow outputs, falling back to the plain `value`
kind; plain cells keep the type-based formatting. Addresses Greptile P2 on #4806.

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

---------

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

* fix(icons): repair broken integration icon rendering (#4810)

* fix(icons): repair broken integration icon rendering

Two distinct bugs left integration icons broken on the /integrations page
(visible at 32-40px, hidden at the toolbar's 16px):

1. Corrupted SVG paths (Notion, Greptile, Granola, Calendly, Grafana, Bedrock):
   over-minified data dropped elliptical-arc flag digits (e.g. `A1 1 0 5.9 7`
   instead of `A1 1 0 0 0 5.9 7`); Granola's cubic stream was truncated. Browsers
   abort path parsing at the first invalid arc flag, so each rendered as a fragment
   or blank. Replaced with correct path data from canonical sources, preserving each
   icon's existing fill/gradient and bgColor.

2. Invisible glyph (Bright Data): its icon uses fill='currentColor' but bgColor was
   '#FFFFFF', and every surface forces text-white on the glyph - white-on-white.
   Changed bgColor to Bright Data's brand blue (#3d7ffc) so the white glyph reads,
   matching the white-glyph-on-brand-chip convention.

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

* fix(icons): restore Calendly dual-tone brand colors

Addresses review feedback: the previous fix replaced the broken Calendly icon
with a monochrome #006BFF path, dropping the cyan #0ae8f0 accent from the
original dual-tone mark. Restored the two-tone logo (blue + cyan) using clean,
valid path data, cropped to a tight square viewBox so it fills the chip.

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

* improvement(icons): enlarge icons, fix Zoom contrast and Quiver chip

- Zoom: glyph was blue-on-blue (#0B5CFF on #2D8CFF chip); switched to
  currentColor so it renders as a white glyph on the blue chip.
- Quiver: chip bgColor #000000 -> #FFFFFF to match the icon's near-white box,
  and enlarged the mark slightly (viewBox crop).
- Enlarged (tightened viewBox, verified no clipping): RevenueCat, Prospeo,
  Granola, Firecrawl, Enrich.so, and the AWS icons (RDS, DynamoDB, SQS,
  CloudFormation, Athena, CloudWatch, SES, Bedrock, S3).
- ZoomInfo left unchanged: it is a full red rounded-square logo that already
  fills its frame, so a crop would clip it.

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

* fix(icons): use Bright Data wordmark on white chip; repair Circleback

- Bright Data: replaced the flame glyph with the official two-tone 'bright data'
  wordmark (provided asset), centered in a symmetric viewBox. Reverted the chip
  bgColor from #3d7ffc to #FFFFFF since the blue wordmark is invisible on a blue
  chip (the wordmark is designed for a light background).
- Circleback: a minifier had rounded the pattern's image scale to scale(0),
  collapsing the embedded logo to zero size (invisible). Restored the correct
  scale (1/280 = 0.00357142857) so the C. mark renders.

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

* fix(docs): sync Quiver block color card to white chip

Reflects the Quiver bgColor change (#000000 -> #FFFFFF) in the docs block info card.

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

* improvement(icons): enlarge AWS/Cloudflare/Dagster icons, fully white Zoom

- Enlarged (tighter viewBox, render-verified, no clipping): Cloudflare, Dagster,
  and the red AWS icons AWS IAM, Identity Center, Secrets Manager, SES, STS.
  Identity Center was anomalously small (filled ~32% of its frame); the group is
  now sized consistently (~80% fill).
- Zoom: the camera lens triangle was still #0B5CFF (blue-on-blue); switched it to
  currentColor so the whole camera renders white on the blue chip.

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

* docs(wiza): consolidate individual reveal into a single operation

Merges the separate Start/Get Individual Reveal operations into one Individual
Reveal operation in the Wiza docs and integrations data (operationCount 5 -> 4).

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

* improvement(icons): size remaining AWS icons to match the set (~80% fill)

Bring RDS, DynamoDB, SQS, CloudFormation, Athena, CloudWatch and S3 up to the
same ~80% fill as the AWS IAM/Identity Center/Secrets Manager/SES/STS group, so
all AWS icons are visually consistent. Bedrock left as-is (already ~92% fill).

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

* fix(icons): use Bright Data flame mark, enlarge ZoomInfo

- Bright Data: the full 'bright data' wordmark was illegible at chip size.
  Replaced with just the flame-'i' brand mark (blue #4280f6 on the white chip),
  centered.
- ZoomInfo: cropped the viewBox toward the white 'Zi' so it's larger; the red
  rounded-square background still fills the chip.

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

* improvement(icons): enlarge CrowdStrike icon

The falcon mark sat small in its chip because the icon used a wide 768x500
viewBox (letterboxed in the square chip). Switched to a square viewBox centered
on the mark so it fills ~80%, consistent with the other icons.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(tables): serialize schema mutations to prevent parallel column clobber (#4812)

* Make workflow description nullable

* fix(tables): serialize schema mutations to prevent parallel column clobber

* fix(tables): load workflow outside schema lock; use DbOrTx for getTableById

* fix(tables): scale idle timeout in updateColumnType to avoid aborting large type changes

* fix(tables): skip stale remap types when workflowId changes concurrently

* fix(tables): scale idle timeout in updateColumnConstraints for large tables

* fix(wait): resume live/draft async waits and preserve cell context on chained waits (#4814)

* Make workflow description nullable

* fix(wait): resume live/draft async waits and preserve cell context on chained waits

* improvement(knowledge): polish tag filter dropdowns

* improvement(knowledge): soften filter section labels

* improvement(knowledge): soften list filter labels

* fix(security): harden SSO domain registration, webhook path isolation, and CSV export (#4813)

* fix(security): harden KB file access, SSO domain registration, webhook path isolation, env secrets, and CSV export

* fix(sso): scope domain conflict query with indexed lower(domain) filter

Address PR review: avoid a full-table scan on every SSO provider
registration by filtering candidate rows in SQL with
lower(domain) = <normalized>, keeping the in-memory ownership check.
Also tighten the normalizeSSODomain TSDoc.

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

* chore: condense env route security comments

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

* icons update

* chore(security): tighten inline comments in CSV export and KB file authorization

Condense verbose comment blocks to concise TSDoc/single-line form; no behavior change.

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

* fix(security): validate internal serve origin in KB file authorization

Replace the bypassable isInternalFileUrl substring check in resolveInternalKbKey
with an origin allow-list (base URL, internal API base URL, TRUSTED_ORIGINS).
A crafted external host whose path is /api/files/serve/<victim-key> no longer
resolves to the victim key. Relative same-origin URLs are unaffected.

* style(sso): use idiomatic sql lower() comparison for domain conflict query

Match the repo's prevailing `sql`lower(col) = value`` idiom for the
case-insensitive SSO domain conflict lookup.

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

* fix(security): align workspace env admin gate with hasWorkspaceAdminAccess

Use the same admin check the secrets UI uses (owner, admin permission, or
org-admin) so owners and org-admins are not wrongly denied their own decrypted
workspace secrets, while read-only members remain restricted to names only.

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

* fix(sso): rely on lower(domain) match for conflict detection, drop dead in-memory recheck

Address PR review: the SQL `lower(domain) = <normalized>` predicate already
excludes rows that the in-memory `normalizeSSODomain(...) === domain` recheck
claimed to catch, making that recheck dead/misleading code. Match on the
canonical lower-cased domain and filter purely by ownership. Malformed legacy
values (wildcards, schemes, ports) never match an email domain at sign-in, so
excluding them is not a gap. Test DB mock now applies the lower() predicate so
the casing-variant case is genuinely exercised.

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

* fix(security): scope webhook deploy path conflict to active webhooks

findConflictingWebhookPathOwner omitted the isActive filter that the
runtime dispatcher (findAllWebhooksForPath) applies, so an inactive but
non-archived webhook from another workflow (e.g. after undeploy or
failure auto-disable) would permanently block any new deployment on that
path even though it never receives deliveries. Align the guard with the
runtime isActive + archivedAt filter; the earliest-owner runtime check
remains the authoritative cross-tenant protection. Also trims verbose
TSDoc on the webhook path-isolation helpers.

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

* fix(security): exclude archived workflows from webhook deploy path conflict

findConflictingWebhookPathOwner now joins workflow and filters
isNull(workflow.archivedAt), matching the runtime dispatcher
(findAllWebhooksForPath). A webhook on an archived workflow can never
receive deliveries at runtime, so it must not block legitimate path reuse
with a 409.

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

* fix(security): anchor KB file ownership to earliest document in any state

A KB file's owner is now the earliest document referencing its key regardless of
state (active/archived/deleted/excluded); access is granted only when that owning
document is still active. Closes the residual where an attacker could plant an
active document to claim a file whose original document was archived or deleted.

* updated greptile icon

* revert(security): drop KB file authorization changes

Reverts the knowledge-base file-access work (origin-pinning / owner-pinning /
origin allow-list in verifyKBFileAccess) and its test. The other hardening fixes
(SSO domain registration, webhook path isolation, workspace env secrets, CSV
export) are unchanged. apps/sim/app/api/files/authorization.ts is restored to its
origin/staging baseline.

* fix(sso): treat caller's own user-scoped provider as owned during conflict check

Self-hosters often register SSO user-scoped via the CLI script (no
SSO_ORGANIZATION_ID). If they later enable organizations and reconfigure the
same domain org-scoped through the UI, the conflict check previously treated
their own user-scoped row as another tenant's and returned a misleading 409.
Recognize the caller's own user-scoped provider as owned so that migration is
allowed, while still blocking another user's or another org's domain.

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

* revert(security): remove workspace-env admin gate

Defer to a credential-based access model (separate change). Restores
GET /api/workspaces/[id]/environment to main behavior and removes the test.

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

* refactor(security): consolidate webhook path-collision check into one helper

Extract findConflictingWebhookPathOwner to lib/webhooks/utils.server.ts as
the single source of truth for cross-tenant path-collision detection, used by
both webhook creation paths (deploy sync and the manual /api/webhooks route).

This also repairs two latent issues in the manual route's previous inline
check, which queried with limit(1) and only webhook.archivedAt:
- limit(1) inspected one arbitrary row, so a same-workflow row could mask a
  foreign collision (false negative). The shared helper scans all matching
  rows.
- It omitted isActive/workflow.archivedAt, so inactive or archived-workflow
  webhooks (which never receive deliveries) permanently blocked path reuse.
  The helper mirrors the runtime dispatcher's filter.

Same-workflow webhook reuse for upsert is now a separate, explicit lookup.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(security): block private/reserved IPs for hosted 1Password Connect SSRF (#4818)

* fix(security): block private/reserved IPs for hosted 1Password Connect SSRF

* test(security): use real isPrivateOrReservedIP and cover IPv6 edge cases

* improvement(integrations): validate and expand devin, cursor, and greptile (#4820)

* improvement(integrations): validate and expand devin, cursor, and greptile

- devin: fix missing org_id path segment on all session endpoints, add 7 session sub-resource tools (list messages/attachments, get/append/replace tags, archive, terminate), pagination, and is_archived output
- cursor: add get_api_key_info, list_models, list_repositories tools
- greptile: align block and docs
- normalize array outputs to default [] and tighten types

* refactor(cursor): simplify list_repositories v2 array normalization

Collapse the redundant `?? []` + `Array.isArray` double-guard into a
single Array.isArray check, per PR review feedback.

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

* fix(devin): scope session-tag mapping to tag ops and normalize array tag inputs

- Only map sessionTags into the tools tags param for append/replace operations,
  preventing stale sessionTags state from clobbering create_session tags
- Fall back to a wired tags value when sessionTags is empty for tag operations
- Normalize tag inputs (string or wired string[]) via normalizeTags so array
  values from other blocks no longer throw on .split

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

* fix(cursor): restore base64 file data in legacy download_artifact metadata

The legacy CursorBlock exposes only content + metadata (no v2 file
output), so metadata.data was the only way legacy-block workflows could
access downloaded artifact bytes. Restore the base64 data field and
document it in the outputs/type instead of dropping it.

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

* fix(devin): coerce terminateArchive to archive flag for boolean-wired input

* docs(integrations): regenerate tool docs for new devin and cursor operations

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(search-replace): don't auto-navigate when content edits invalidate the active match (#4819)

* fix(search-replace): don't auto-navigate when content edits invalidate the active match

* fix(search-replace): clear afterReplaceIndexRef on apply failure and zero matches

* fix(search-replace): remove duplicate setActiveSearchTarget(null) on close

* fix(search-replace): move afterReplaceIndexRef write inside handleApply past the guard

* fix(search-replace): auto-navigate when hydration resolves with no prior active match

* chore(search-replace): remove inline comments

* fix(search-replace): revert !activeMatchId guard that caused immediate re-navigation after deselect

* improvement(enrichments): limit company-info to fields both providers return (#4817)

Hunter's company dataset returns null industry/foundedYear for many large companies (verified against the live API for Microsoft, Amazon, Google), so under the first-non-empty-wins cascade those columns appeared inconsistently across rows. Limit company-info outputs to employee count and description — the fields Hunter and PDL both reliably return — so every row is consistent. employeeCount is a string so Hunter's range bucket and PDL's exact count share the column.

* fix(files): don't reject external URLs containing '..' in file parse validation (#4821)

* fix(files): don't reject external URLs containing '..' in file parse validation

The file block's file_fetch operation rejected any external URL whose path
contained '..' (e.g. Slack files-pri slugs with a literal '...') with
'Access denied: path traversal detected'. Traversal checks only apply to
local paths — external http(s) URLs are fetched with SSRF protection
downstream and are never resolved against the filesystem, so they now
short-circuit as valid. Internal /api/files/serve/ URLs keep full traversal
protection.

* test(files): fix external-URL assertion to handle undefined error

* test(files): assert success explicitly in external-URL traversal test

* fix(files): keep traversal protection for https URLs matching internal serve paths

* feat(google-sheets): add row filtering to read with numeric operators (#4822)

* feat(google-sheets): add row filtering to read with numeric operators

Adds client-side row filtering to the Google Sheets read (v2) operation.
Filter the returned rows by a header column using text operators
(contains, not_contains, exact, not_equals, starts_with, ends_with) and
numeric/ordering operators (gt, gte, lt, lte). Filtering lives in a pure,
unit-tested helper (filterSheetRows) and runs over the fetched read range;
an optional `filter` output reports whether the column was found and how
many rows matched.

Also hardens the surrounding tools:
- trim spreadsheetId in write/update/append URL builders (matches read)
- URL-encode the v1 read default range
- expose valueInputOption for the update operation in the block

Backwards compatible: with no filter requested, read output is byte-
identical and the `filter` field is omitted. The filterMatchType union is
widened additively (4 -> 10 values).

* fix(google-sheets): correct filter metadata for missing column and header-only sheets

- matchedRows is now 0 (not totalRows) when the filter column is not found,
  so it no longer contradicts applied=false / columnFound=false
- columnFound now reflects an actual header lookup for empty/header-only
  sheets instead of being hardcoded true
- add tests covering header-only and empty sheets with present/absent columns

* fix(selectors): fetch all pages for paginated dropdown list routes (#4823)

* fix(selectors): fetch all pages for paginated dropdown list routes

Dropdown selectors fetched only the first page of paginated provider
APIs, silently hiding results past page one. Add bounded server-side
draining to the list routes across Microsoft Graph, Google, Notion,
Atlassian, Linear, AWS CloudWatch, and offset/token REST APIs, plus a
shared client-side drain cap in the selector hook. Response shapes,
stored values, and tool execution are unchanged; CloudWatch list tools
still honor a caller-supplied limit. Also fixes the Word file picker
that was searching for .xlsx files.

* fix(selectors): harden JSM and Monday pagination draining

- JSM service-desk/request-type drains advance `start` by the actual row
  count returned (not the fixed page size) and stop on an empty page, so a
  short non-final page can't skip items.
- Monday boards drain now checks `response.ok` per page, surfacing a
  mid-drain HTTP failure instead of treating it as an empty final page and
  returning a partial 200.

* docs(selectors): clarify JSM drain advances start by actual row count

The offset-advancement fix (advance `start` by the rows returned, not the
fixed page size) landed in 7b19788a8; update the TSDoc to match so it no
longer reads as advancing by `limit`.

* fix(selectors): drain fetchPage in direct fetchList callers

Making `fetchList` optional left three direct callers (outside the
useSelectorOptions hook) calling it unguarded, which broke the build's
type check. Route them through a shared `loadAllSelectorOptions` helper
that uses `fetchList` when present and otherwise drains `fetchPage`.
This also prevents a regression: `confluence.spaces` / `knowledge.documents`
now paginate via `fetchPage` only, and these callers (search/replace,
value resolution) would otherwise have silently returned no options.

* chore(selectors): rename MAX_PAGE_PAGES to MAX_NOTION_PAGES for readability

* fix(sso): re-check domain conflict before write and reject IP-address domains (#4825)

* improvement(copilot): make copilot_messages the sole transcript store, remove JSONB dual-write (#4826)

Stop writing/reading the legacy copilot_chats.messages JSONB column now that
reads are cut over to copilot_messages. Make appendCopilotChatMessages the
primary write (throws on failure instead of swallowing), repoint peripheral
readers (workspace VFS, chat cleanup, data drains, fork, superuser import) to
copilot_messages, and persist the assistant turn inside finalizeAssistantTurn's
transaction so it commits atomically with the stream-marker clear. The column
itself is dropped in a follow-up migration after this bakes.

* feat(tables): expand filter operators (not-contains, starts/ends-with, not-in, empty) (#4827)

Add does-not-contain ($ncontains), starts-with ($startsWith), ends-with
($endsWith), not-in-array ($nin, previously executed server-side but unexposed
in the UI), and is-empty/is-not-empty ($empty) filter operators end-to-end —
SQL builder, condition types, query-builder converters/constants, the filter
UI, the Table tools/block descriptions, and docs.

Also fix correctness bugs in the filter builder surfaced by the wider operator
set:
- Same-column AND rules (e.g. age > 18 AND age < 65, or name startsWith 'A'
  AND name endsWith 'Z') silently overwrote each other because the AND group
  was keyed by column name. They now merge into one operator object, which
  also makes Filter -> rules -> Filter round-trip losslessly for multi-operator
  columns.
- $nin values were not split into an array like $in, and textual-match values
  like "123" were numeric-coerced (breaking the ILIKE path).
- A non-boolean $empty operand from the raw API silently inverted the check; it
  now coerces 'true'/'false' strings and otherwise returns a 400.

* improvement(copilot): stop persisting tool-call result outputs in transcripts (#4829)

Opening a Mothership task could take many seconds because a single persisted
assistant message in copilot_messages.content can reach hundreds of MB, almost
entirely inside contentBlocks[].toolCall.result.output (e.g. a get_workflow_logs
or run_workflow result). The DB query is ~2ms; the cost is detoasting that
payload, shipping it to the browser, and parsing it.

These outputs are dead weight on the Sim side: they are never rendered (the
thread shows only tool name/title/status) and never replayed to the model (the
upstream copilot service owns conversation memory). So drop result.output before
it is persisted, keeping result.success/error plus the tool metadata.

- add stripToolResultOutput() in persisted-message.ts
- apply it in messages-store toRow (covers every write path) and in
  loadCopilotChatMessages (existing rows render fast on read)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* feat(providers): add Together AI, Baseten, and Ollama Cloud model providers (#4830)

* feat(providers): add Together AI, Baseten, and Ollama Cloud model providers

* fix(providers): guard Ollama streaming fast-path with hasActiveTools

Match Together/Baseten/Fireworks: when tools are supplied but all are
filtered out (usageControl 'none'), take the single streaming call instead
of an extra non-streaming round-trip.

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

* fix(providers): filter non-chat model types from Together model list

* refactor(providers): dedupe Ollama Cloud upstream schema

ollamaCloudUpstreamResponseSchema was byte-for-byte identical to
ollamaUpstreamResponseSchema (both /api/tags endpoints return the same
{ models: [{ name }] } shape). Drop the duplicate and reuse the shared schema.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(knowledge): calendar view sync, deduplicate popover animation classes, type-safe filter cast

* cleanup(knowledge): remove TRIGGER_BORDER_CLASS duplication, inline displayLabel, drop enabledFilterParam alias

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com>

* feat(blocks): add BlockMeta to Quiver and Linq; fix invalid block config fields; update skills

Block fixes:
- Add QuiverBlockMeta (tags + 3 templates: icon generator, diagram creator, vectorizer)
- Fix QuiverBlock: remove invalid tags field from BlockConfig, IntegrationType.Design →
  IntegrationType.AI (Design doesn't exist in the enum)
- Fix GreptileBlock: remove invalid tags field from BlockConfig,
  IntegrationType.DeveloperTools → IntegrationType.DevOps
- Fix LinqBlock: remove invalid tags field from BlockConfig (tags belong only in BlockMeta)

Skills:
- add-block: add dedicated BlockMeta section with structure, rules, and registration
  pattern; add BlockMeta checklist items
- add-integration: add BlockMeta to block structure template, add rules clarifying
  that tags must NOT appear on BlockConfig and integrationType must be a valid enum
  value; update registry snippet to include blocksMeta; add checklist items

* fix(integrations): fix category dropdown by defining missing LANDING_INTEGRATIONS_DATA_PATH and regenerating integrations.json

The staging merge introduced landing-content.ts but forgot to define
LANDING_INTEGRATIONS_DATA_PATH in generate-docs.ts, causing the script
to crash before writing integrations.json.

The stale JSON had integrationTypes (plural array) from an older script
version, while the Integration type and workspace UI both read
integrationType (singular string) — so ALL_CATEGORY_SECTIONS bucketed
to undefined and the category filters never appeared in the dropdown.

Fixed by adding the missing path constant and re-running the generator.
integrations.json now has 192 entries with the correct integrationType field.

* fix(sidebar): restore resize handle on all pages

commit 3109104582 wrapped the resize handle in {(isCollapsed ||
isOnWorkflowPage) && ...} and added a useEffect that resets sidebar
width to SIDEBAR_WIDTH.MIN whenever the user navigates away from a
workflow page. Together these made the sidebar non-resizable on Tasks,
Tables, Knowledge Base, and every other non-workflow page.

Restore the staging behavior: always render the resize handle and
remove the effect that forced the width reset on page transitions.

* fix(sidebar): match staging onKeyDown and tabIndex on resize handle

The resize handle was still conditionalizing onKeyDown and tabIndex
on isCollapsed, blocking keyboard accessibility of the separator role
when expanded. Staging always attaches both unconditionally.

onKeyDown={isCollapsed ? handleEdgeKeyDown : undefined} → onKeyDown={handleEdgeKeyDown}
tabIndex={isCollapsed ? 0 : undefined}                 → tabIndex={0}

* feat(integrations): show connected credentials on integration detail page

When navigating to /integrations/google-docs (or any integration), a
Connected section now appears above the templates listing all workspace
credentials tied to that provider. Each row links back to the credential
detail page (/integrations/connected/${id}) for management actions.

Pairs with the earlier change that routes connected items from the
integrations list to the provider detail page instead of directly to
the credential detail page.

* fix(integrations): rename Add in chat to Add to Sim

* fix(skills): rename Add button to Add to Sim

* fix(platform): restore M1/M2/M3 regressions and LazyMotion on landing page

M1 — Invitation guard: re-introduce usePermissionConfig().isInvitationsDisabled
alongside the workspace inviteDisabledReason check. The flag now also
respects NEXT_PUBLIC_DISABLE_INVITATIONS and EE permission-group
disableInvitations, not just billing policy.

M2 — Settings redirects: /settings/integrations and /settings/skills
now server-redirect to /integrations and /skills respectively so old
bookmarks and emails don't silently land on General.

M3 — Starter block search exclusion: restore block.type !== 'starter'
guard in the search store so users cannot add a duplicate Starter block
via the command palette.

LazyMotion: restore LazyMotion + domMax/domAnimation wrappers and m.*
components in landing-preview-panel and landing-preview-home. The
removal was accidental (the full motion bundle was left after an
import cleanup), which caused the entire framer-motion feature set to
load eagerly on the landing page.

* fix(integrations): revert connected list to credential detail; remove settings redirects

* feat(sidebar): restore workspace switcher search with updated styling

Shows a search input in the workspace dropdown when the user has more
than 3 workspaces (WORKSPACE_SEARCH_THRESHOLD). Keyboard navigation:
ArrowDown/Up to move through results, Enter to switch, resets on close.

Styled to match the current branch (border-1/surface-5 tokens, sm text,
11px Search icon) rather than the old staging styles. Highlight state
is wired through chipVariants active prop so it follows the same active
appearance as clicked/hovered items.

* fix(sidebar): clean up workspace search — layout, memo, and effect guard

* fix(sidebar): align workspace rename input selection style with workflow rename

* perf(sidebar): eliminate React re-renders during sidebar drag resize

Previously, every mousemove during resize called setSidebarWidth(), which
both updated the --sidebar-width CSS variable (sync) and set Zustand state
(async). This caused:
  1. A 1-frame transition flash on mousedown — isResizing state had to
     round-trip through React before the is-resizing CSS class was applied,
     so the width transition fired for the first pixel of movement.
  2. A React re-render per pixel dragged — components reading sidebarWidth
     from the store (avatars, usage-indicator) lagged one frame behind the
     container, making the + and ... buttons appear to jump ahead.

New approach:
  - handleMouseDown adds is-resizing directly to the sidebar DOM node before
    any React involvement (synchronous, no frame lag).
  - mousemove writes only to the CSS custom property (zero React renders).
  - mouseup persists the final width to Zustand exactly once.
  - isResizing / setIsResizing state removed from the store and hook — they
    are no longer needed since the class is managed via direct DOM mutation.

* perf(sidebar): add requestAnimationFrame throttle to resize mousemove handler

* fix(sidebar): fix drag-right lag caused by WorkspaceChrome overflow-hidden transition

The sidebar-container's is-resizing class correctly suppressed its own width
transition, but the two wrapper divs in WorkspaceChrome both have
transition-[width]/transition-transform with a 175ms ease. The outer wrapper
also has overflow-hidden, so while the sidebar content was at the correct width
instantly, it was visually clipped by the outer wrapper which was still
animating — causing the + and ... buttons to appear to lag behind the resize
line on drag-right (not on drag-left, since shrinking doesn't clip content).

Fix: add sidebar-shell-outer/sidebar-shell-inner class names to both chrome
wrappers, and suppress their transitions via html.sidebar-resizing rule when
a drag is active. The html.sidebar-resizing class is toggled directly in the
resize hook alongside is-resizing, so it takes effect synchronously on mousedown.

* fix(icons): redesign Download icon to match Upload style; fix Upload/Download confusion

Download icon was missing the tray/shelf line at the bottom that Upload has,
making it look like a plain arrow rather than a matched pair. Updated Download
to use the same viewBox, stroke weight, and three-path structure as Upload
(tray + stem + arrowhead), just pointing down.

Also fix 5 places where Upload (↑) was incorrectly used for download/export
actions:
  - files.tsx: two Download action rows in toolbar and context menu
  - tables/table.tsx: Export CSV toolbar button
  - table-context-menu.tsx: Export CSV context menu item
  - logs.tsx: Export toolbar button
  - landing-preview-logs.tsx: decorative Export button

Import CSV and actual upload actions correctly keep the Upload icon.

* fix(icons): replace Upload with Download on all remaining export/download actions

- panel.tsx: Export workflow dropdown item
- context-menu.tsx: Export in sidebar workflow context menu
- chat.tsx: Export chat button
- output-panel.tsx: Export console CSV button
- terminal.tsx: Export console CSV button
- resource-content.tsx: Export table as CSV + Download file buttons

* fix(icons): fix remaining Upload→Download on download actions in files and logs

- action-bar.tsx: download button in files toolbar
- file-row-context-menu.tsx: Download item in file context menu
- file-download.tsx: both download buttons in log details file viewer

* updated block skills, settings pages, modals, buttons -> chips, blocks missing metadata

* updated skill modal

* improvement(resource-header): refine breadcrumb truncation ux

* Fixes

* improvement(resource): add floating overflow text tooltips

* wire up credits counter

* improvement(resource-header): mute path dropdown title

* refactor(resource-header): share floating-tooltip engine, prune dead overlay tooltips (#4844)

Clean up the breadcrumb truncation feature for reuse and correctness:

- Extract useFloatingTooltip / useIsOverflowing / FloatingTooltip into a shared
  floating-tooltip module. BreadcrumbSegment and FloatingOverflowText now consume
  one implementation instead of duplicating ~150 lines of positioning, velocity,
  overflow-detection, and portal logic.
- Replace the hardcoded terminal-label regex in ResourceHeader with a typed
  `terminal` flag on BreadcrumbItem (set by the document chunk/loading crumbs),
  decoupling the generic header from knowledge-base copy.
- Clear the path-popover close timeout on unmount and reuse the shared
  POPOVER_ANIMATION_CLASSES constant.
- Drop the redundant manual overflow-state writes (fixes a sticky fade mask).
- Revert FloatingOverflowText inside Combobox `overlayContent` back to plain
  truncating spans across files/logs/tables/scheduled-tasks/document: the combobox
  overlay is pointer-events-none, so the tooltip handlers never fired there.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(emcn,resource-header): address PR #4844 review feedback

- useIsOverflowing now uses a callback ref so the ResizeObserver follows the
  element across mount/unmount/reassignment instead of capturing it once at mount.
  Safe for conditionally rendered consumers of the shared hook. (greptile P2)
- Move POPOVER_ANIMATION_CLASSES out of chip-date-picker implementation internals
  into emcn/components/popover/popover-animation.ts, exported from the
  @/components/emcn barrel. Consumers now import from the module boundary.
  (greptile P2)

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

* Prompt surface

* Regenerated contracts

* upgrade table and styling upgrade

* Literalness gaps

* Updates

* Remove prefix for running code

* Display names for reads

* Tool catalog

* fix schema to include integration

* fix(files): align delete icon with tables view (Trash → Trash2)

Co-Authored-By: waleed <waleed@simstudio.ai>

* fix(mothership): preserve blockType for integration contexts in sent messages

Integration mention chips were missing their provider icons in sent messages
because blockType was dropped when mapping ChatContext to messageContexts.
renderIntegrationTile returns null without blockType, silently hiding the icon.

* fix(mothership): allow 'integration' resource type in chat resources API

The VALID_RESOURCE_TYPES allowlist was missing 'integration', causing a
400 error when adding integrations to the Mothership resource tab — so
they never persisted and disappeared on refresh.

* fix(ui): Add "File" title next to file resource header

* fix(ui): fix resource header columns being bolded

* fix(resource): keep the floating tooltip from jumping on click

Gate the focus-driven show behind :focus-visible so a mouse click (which
focuses the trigger) no longer re-shows the tooltip anchored to the element's
bottom edge. On click the tooltip now hides cleanly instead of jumping down;
keyboard focus still shows it.

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

* perf(sidebar): eliminate unnecessary re-renders in workspace switcher for non-search users

- onMouseEnter: only set highlightedIndex when showSearch is true, preventing
  a state update + re-render on every workspace row hover for users with ≤ 3
  workspaces where the search is never shown
- onOpenChange: only reset workspaceSearch and highlightedIndex when showSearch
  is true, since both values are always already at their defaults for non-search
  users and setting them triggers a pointless re-render during dropdown close
- data-workspace-row-idx: only set when showSearch is true since the scroll
  effect that reads this attribute is already gated on showSearch

* feat(search): context-aware cmd-k results on the integrations page

When cmd-k is opened on the integrations page, show two new result
groups: connected accounts (visible even with empty input) and catalog
integrations (appear once the user types). Selecting an OAuth integration
deep-links to its detail page with ?connect=oauth so the connect modal
auto-opens. Non-OAuth integrations navigate to the plain detail page.

Both groups are gated to the integrations page only and respect the
hideIntegrationsTab permission. The credentials fetch shares the same
React Query cache key as the integrations page itself (no double fetch).

* refactor(emcn): make the floating tooltip the one canonical Tooltip

Replace the Radix-based emcn Tooltip with the cursor-following floating tooltip so
every tooltip in the app uses one consistent style. Built on the shared
floating-tooltip engine (relocated into emcn), not a parallel implementation.

- Move the floating-tooltip engine into emcn/components/tooltip and export it from
  the barrel; re-point its consumers (FloatingOverflowText, resource-header)
- Extend the FloatingTooltip bubble to render arbitrary children (+ role/id for
  a11y) so it can back general tooltips, not just overflow text
- Rebuild emcn Tooltip (Root/Trigger/Content/Provider/Shortcut/Preview) on
  useFloatingTooltip — compound API preserved, ~350 call sites unchanged, legacy
  side/align props accepted and ignored (the tooltip follows the cursor). Removes
  @radix-ui/react-tooltip usage (package kept for a later cleanup; react-slot
  retained for asChild)

Note: general tooltips now show instantly (no hover delay) and follow the cursor.

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

* style(emcn): put tooltip text on the design scale (text-caption)

Replace the tooltip's ad-hoc `text-xs` + `leading-[18px]` with the semantic
`text-caption` (12px) font-size token so the text styling is fully on the design
scale and self-documenting, matching how the rest of the system is set up. The
color already used the global `--text-body` token. No visual change (still 12px
with a ~18px line height).

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

* Mship byok

* feat(sidebar): add empty task state and inline task creation

- Show "No tasks yet" in the Tasks section (expanded and collapsed) when the list is empty
- Clicking + now creates a task via the API and navigates directly to it, rather than navigating to home
- Add isCreatingTaskRef guard to prevent double-click from spawning multiple tasks
- Disable + button while creation is pending
- Fall back to home navigation on creation error

* invite, billing, home

* Sandbox warning

* improvement(seats): auto purchase seats on invitations into workspace (#4857)

* improvement(seats): auto purchase seats on invitations into workspace

* improve sampling for seat drift reconciler

* address comments

* feat(knowledge): align connector UI with integrations page styling

- ConnectorTypeCard now matches integration rows: brand-colored rounded-xl tile, ArrowRight, title/subtitle hierarchy
- ConnectorCard icon upgraded from flat surface-4 to branded tile (white icon on brand bg, graceful fallback)
- Connector header badges use chipVariants instead of custom Button classes
- Add-connector search input aligned to integrations style (h-[30px], rounded-lg, border-1)

* fix(icons): trim Folder SVG viewBox to remove right-side whitespace

The folder path only extends to x≈14.33 in a 15-unit viewBox, leaving
~0.5 units of empty space on the right. At 12px rendered size this
produces ~0.4px extra gap (visible as ~1px on retina displays) compared
to solid icons like the workflow color square. Trimming the viewBox to
14.5 units makes the folder fill its chip slot evenly.

* fix(user-input): restore draft text synchronously to preserve contexts on nav

The SSR-safe approach (empty useState + effect restore) created a timing
window where the sync effect in useContextManagement fired with message=''
before the value was set, clearing any restored contexts. Folder and workflow
contexts (not re-added by applyAutoMentions) were lost on every nav-back.

Revert to the staging approach: initialize value synchronously from the
draft store so message is already populated when effects run, matching
the behavior on staging.

* fix(queue): render context chips in queued messages

- Remove plainMentions from queued message rows so context chips render
  with icons, consistent with sent messages
- Fix computeMentionRanges to use '/' prefix for skill contexts (content
  has the slash trigger restored at submit time, not '@')

* fix(mothership): remove integrations from add-resource dropdown

* fix(mothership): comment out integrations from add-resource dropdown

* File serializer

* Skills

* chore(db): drop form, templates, template_creators, template_stars tables

These tables backed the Forms and Templates platform features which were
intentionally removed from this branch. Clean up the DB schema to match.

* chore(db): add migration metadata for 0224 drop tables

* block icons, sidebar, toolbar

* chore: remove remaining dead code for template-profile feature

- Remove 'template-profile' from SettingsSection union type
- Remove 'template-profile' entry from SECTION_TITLES
- Remove now-redundant template-profile guard in settings sidebar
- Remove commented-out template-profile nav item

* fix(multi-select): preserve anchor on range selection for tasks and folders

After a shift+click range, the anchor (lastSelectedTaskId / lastSelectedFolderId)
was being updated to the end of the range (toId). This caused subsequent
shift+clicks to extend from the wrong point instead of the original click.

Standard behavior: anchor stays at the initial click (fromId) so repeated
shift+clicks always expand/contract relative to where you started.

* Update skills

* Load skill tool

* Fix vfs dynamic context encoding

* Media subagent

* feat(emcn): add SearchInput component and unify search bars platform-wide

- Add SearchInput to emcn: 30px chip-family filled search input matching the
  integrations page pattern (border-1, surface-5, leading Search icon)
- Migrate all 22 search bars across settings, EE pages, and integrations to
  SearchInput (only layout classes allowed at callsites)
- Rename Sim Keys -> Sim API Keys in nav/title; page copy now says API key
- Remove components/ui input, label, and verified-badge; migrate consumers
  to emcn equivalents or raw inputs (table cell editor, wand prompt bar)
- Delete dead EE skeleton files (data-drains, data-retention)
- General settings: Home Page chip moves to header left as navigation

* fix(files,tables): restore new-file editor autofocus and CSV import error toasts

Both were dropped on the staging line and regressed vs production (main):

- Files: the new-file editor autofocus chain (files.tsx -> file-viewer ->
  text-editor) was stripped by the react-doctor dead-code pass in #4544,
  which misread the prop-drilled `autoFocus` (consumed by an imperative
  `editor.focus()` effect) as unused. Restored the prop through all three
  layers and the one-shot focus effect so creating a new file focuses the
  editor immediately.
- Tables: CSV import failures were silently logged with no user feedback.
  Restored the per-file and generic `toast.error` surfacing.

* Move superagnet back into superagent

* feat(home): score suggested actions by workspace signals

- Derive the suggestion pool from the curated block template catalog
  (1,343 prompts across 172 blocks) instead of 15 hardcoded entries
- Fix inverted relevance: prompts for connected providers are now boosted
  4x (instantly runnable) instead of excluded; unconnected discounted 0.4x
- Weight by featured (3x), popular category (1.5x), and resource gaps
  (no tables -> boost table starters; has KBs -> dampen KB-creation prompts)
- Weighted sampling without replacement, max one suggestion per block
- Connect rows weighted by catalog template count; 2 for fresh workspaces,
  1 once something is connected
- Key the catalog map by both versioned and base block types so gmail_v2
  templates resolve (gmail, github, notion, linear were silently dropped)
- Replace derive-in-effect state with a useMemo keyed by a shuffle nonce
- Add suggested_action_clicked / suggested_actions_shuffled /
  suggested_actions_toggled PostHog events

* billing, teammates

* improvement(credentials): credentials invites, secrets tab wiring up (#4874)

* improvement(credentials): move away from invite notion

* wire up secrets ui/ux

* address comments

* get consistent styling by removing emcninput + text area

* styling consistency

* remove fallback

* address comment:

* refactor(ui): migrate settings & workspace UI to chip design system

Migrate modals to ChipModal (showDivider, hint, resizable, size, leading,
ChipModalTabs), standardize Chip variants, add ChipCombobox wrapper, and apply
chip inputs/dropdowns across settings, knowledge, logs, tables, inbox, EE tabs.
Render ChipModalTabs as a ChipSwitch segmented control. Align /settings/secrets
detail with /integrations, refresh whitelabeling, and restore file-editor
autofocus and CSV import error toasts.

* Update media

* fix(mothership): restore integrations to useAvailableResources for @ mention

Integrations were fully removed from useAvailableResources which broke
the @ mention menu since user-input shares the same hook. Now integrations
are always included in the hook but excluded at the AddResourceDropdown
component level, keeping them out of the sidebar + menu while remaining
available for @ mention autocomplete.

* refactor(settings): chip design-system consistency pass across all tabs

Extract a shared chip-field shell (CHIP_FIELD_SHELL/CHIP_FIELD_INPUT) mirroring
Input variant='chip' and route secrets, credential detail, and integrations
credential detail through it (30px height, font-medium, focus ring). Add a
Discard action to the secrets header when dirty. Group BYOK providers into
Models/Search & web/Enrichment sections and align its tiles to the integrations
tile.

Normalize list-row typography to text-[14px]/text-[12px] and icon tiles to
rounded-xl + border across api-keys, copilot, custom-tools, mcp,
workflow-mcp-servers, credential-sets, and access-control. Tone the secrets
Details chip and per-row affordances to ghost. Fix token correctness: raw
tailwind colors to design tokens (data-drains), missing chip variant on the
Snowflake role input, ColorInput chip-field reuse (whitelabeling), error token
and icon sizes (workflow-mcp-servers), border token (mcp), no-results sizing
(secrets), row chrome (recently-deleted), hover token and Button to Chip
(access-control), and deduped textarea chrome (sso).

* feat(settings): unify filter dropdowns on ChipSelect (integrations style)

Add a ChipSelect emcn component — a filled chip trigger + chevron opening a
DropdownMenu, matching the integrations category filter — supporting single
select, multi-select (checkbox rows), grouped options, and optional in-menu
search. Migrate every settings/EE filter dropdown off ChipCombobox to it:
audit-logs (resource-type multi + time-range), data-retention, data-drains,
general, admin (grouped tool picker), inbox status filter, and the workflow
MCP-server pickers. The SSO provider-id field stays an editable combobox since
it accepts free-text slugs.

Also fix audit findings: chip the MCP client-secret input, normalize an MCP
error-text size, and drop now-dead destination-icon code in data-drains.

* fix(mentions): require explicit @ for integration mentions; decorate sent messages robustly

- Bare integration names in prose (Monday, Notion, Clay) are no longer
  auto-converted to mentions or chipped — mention treatment is strictly
  opt-in via a token-starting @ (fixes the scunthorpe problem)
- @-prefixed mentions still canonicalize casing (@slack -> @Slack) on both
  the keystroke fast-path and bulk paths (paste, template, draft, STT)
- Sent/queued messages now self-sufficiently decorate @IntegrationName
  tokens via a text scan, covering messages sent before the input pass
  ran or authored outside the chat input
- Integration contexts missing a resolvable blockType (messages persisted
  before blockType was saved) are backfilled by label lookup so their
  mention pills render the brand icon again

* refactor(settings): section the API keys page like secrets

Wrap Workspace, Personal, and the allow-personal-keys toggle in SettingsSection
(muted label + divider) instead of bare bold headers, matching the secrets and
BYOK pages.

* fix(settings): ChipSelect renders above modals + full-width form mode

Raise the ChipSelect menu to --z-popover so it layers above modal surfaces
(--z-modal) instead of opening behind them. Add a fullWidth prop that stretches
the trigger and right-aligns the chevron for form-field use, and apply it to the
workflow MCP-server pickers.

* fix(emcn): ChipSelect uses the emcn flat chevron, not lucide's square one

The lucide ChevronDown is square; rendering it at the chip's 9x7 footprint
stretched it. Switch to the custom emcn ChevronDown (built for that wide aspect),
matching the integrations filter and ChipDropdown.

* fix(emcn): ChipSelect trigger hugs its content (w-fit)

In a stacked form layout the trigger was stretched by align-items: stretch,
leaving an empty gap to the right of the value. Add w-fit so the chip sizes to
its content (a compact pill) everywhere; fullWidth form selects are unaffected.

* fix(emcn): ChipSelect uses a square lucide chevron

Revert to lucide's ChevronDown sized square (size-[14px]) so it renders crisp,
matching the standard select chevron used by Combobox.

* renamed tasks to chats

* rename and file change

* Wspace resource refs

* improvement(billing): wire up billing, org, teammates tabs + remove deprecated subscription tab (#4887)

* improvement(billing): wire up billing, org, teammates tabs + remove depr subscription tab

* pass exec timeout to tool routes

* reuse helper

* address comments

* address disable comment

* Vfs updates

* chore(db): remove migration 0224 to regenerate on top of staging

Co-authored-by: Cursor <cursoragent@cursor.com>

* VFS updates and linter

* fix type errors and regen migration?

* File block v5

* Remove docs

* Fix

* Doccer image

* Nuke the dag

* chore(db): drop branch migration 0226 ahead of staging merge; will regenerate

* chore(db): regenerate migration 0226 after staging merge

* Fix lint

* externalize before compaction in fallback'

* Run workflow improvements

* Loosen run options validation

* Lint

* fix save/discard chips to be consistent

* fix(ui): remove smodal tabs in favor of chip modal tabs

* update byok manager component

* fix(ui): skip auto-scrolling on mouse highlight of workspace

* Add creds to wspace context

* Wscontext

* fix(platform): restore settings redirects, forgot-password Enter submit, and tag tooltip visibility

- Re-add SETTINGS_REDIRECTS so /settings/integrations and /settings/skills
  deep links redirect to their top-level routes instead of rendering an
  empty settings panel (accidentally removed in 86da193cc3 one minute
  after cca5054cf6 added it)
- Add opt-in onSubmit to ChipModalField input/email variants and wire it
  in the forgot-password modal so Enter submits again (lost in the
  ChipModal conversion)
- Knowledge tag tooltip: drop the max-h/overflow-y-auto clamp that the
  pointer-events-none floating tooltip made unreachable; truncate each
  tag row instead so all tags stay visible with bounded height

* feat(usage-limits): org member specific limits (#4893)

* feat(usage-limits): org member specific limits

* revert feature flags

* fix type issues

* address comments

* address comments and address other billing gapes'

* fix

* improvements

* fix remaining credits display

* fix

* pass ws id to route validations

* chore(db): remove migration 0226_third_spot before staging merge

* chore(db): regenerate migration as 0227 after staging merge

* Block

* feat(telemetry): add posthog + audit coverage for new platform actions

Audit log (compliance/permission-relevant only):
- org_seat.provisioned — seat auto-purchased when an invite acceptance
  grows the org (actor = accepting user, includes seat delta)
- org_plan.converted — Pro→Team conversion triggered by invite acceptance
- org_seat.drift_reconciled — hourly cron healed a drifted seat count
- credential_member.added/removed/role_changed — credential sharing
  surface was previously fully unaudited
- table.created — parity with existing table.updated/deleted
- skill updates now record skill.updated instead of mislabeled
  skill.created

PostHog:
- seats_provisioned, credential_shared/unshared,
  environment_updated/deleted (key counts only, never names/values)
- table_import_started/completed — background CSV imports previously had
  zero failure observability
- table_exported, file_downloaded, skill_updated
- credential_connected now fires for OAuth completions (draft-hooks),
  credential_deleted for OAuth disconnects; previously only manual
  credentials were tracked
- table_workflow_run gains deployment_mode (live/deployed/mixed)

Also: logger.warn on all new credential-admin 403 denials (members +
environment routes); invite-created audit enriched with
enforcedFixedSeats/plan.

Deliberately excluded as noise: per-hour dead-letter audit rows (would
re-record the same stuck event every cron run) and a duplicate
system-actor org_plan.converted in the Stripe outbox handler.

* feat(home): fill textarea on suggested prompt click instead of sending

Clicking a prompt action in the Suggested Actions panel now populates
the Mothership user-input textarea (via applyAutoMentions) and focuses
it with the caret at end, rather than immediately submitting. The user
can review, edit, and send manually.

* fix(templates): name owning integration in featured block template prompts

Four featured prompts omitted their owning integration name, making them
unbranded and disconnected from their title. Each prompt now explicitly
names GitHub or Google Sheets so mentionifyIntegrations renders the chip
and the copy reads as a self-contained agent-building instruction.

* fix(templates): rewrite fragment prompts so each names its integration and reads as a complete instruction

Featured and non-featured block template prompts that either omitted
the owning integration's canonical name or were phrased as marketing
fragments rather than natural user instructions have been rewritten.
Each updated prompt now starts with an imperative verb ("Build a
workflow that…"), names the owning integration explicitly so the
@-mention chip renders correctly, and aligns with the entry's title.

Files changed: salesforce, hubspot (×2), github, slack, airtable,
firecrawl, iam.

* fix(templates): name integration in remaining block template prompts

- google_docs.ts: replace 'Google Doc' with 'Google Docs document' in 4 prompts; update title 'Meeting notes to Google Doc' to 'Meeting notes to Google Docs'
- google_sheets.ts: replace 'Google Sheet' with 'Google Sheets spreadsheet/Google Sheets' in 3 non-featured prompts
- slack.ts: replace 'Google Doc' with 'Google Docs document' in 'Daily standup summary'
- stripe.ts: replace 'Google Sheet'/'Slacks' with 'Google Sheets'/'Slack' in 'Weekly metrics report'
- reddit.ts: add 'Reddit' to 3 prompts that only referenced subreddits
- notion.ts: rewrite featured prompt to start with a verb and name Notion
- jira.ts: rewrite featured marketing-fragment prompt to start with a verb
- linear.ts: rewrite featured marketing-fragment prompt to start with a verb
- gmail.ts: rewrite featured marketing-fragment prompt to start with a verb and name Gmail

* fix(icons): convert monochrome dark brand icons to currentColor for dark mode

LinkupIcon, InfisicalIcon, IntercomIcon, LumaIcon, GranolaIcon, OnePasswordIcon,
and RailwayIcon were hardcoded to black/near-black fills/strokes, making them
invisible in bare DARK mode. Convert all to currentColor so they follow the
theme-aware text color.

Add iconColor: '#286efa' to IntercomBlock (Intercom brand blue is a confident
mid-tone, safe on both themes).

Two-tone icons (StagehandIcon, AgentPhoneIcon, QuiverIcon) are left unchanged
because their white details are structural — converting to single-tone would
destroy logo legibility.

* removed color, user input, sidebar, suggested actions, chips/emcn

* removed color migration

* Table bump

* improve(blocks): audit block catalog metadata for accuracy and fill gaps

- Fix template prompts that claimed capabilities blocks don't have
  (fabricated triggers, non-existent tools) across ~98 integrations
- Normalize integration tags to family conventions and valid union values
- Align alsoIntegrations and modules with template prompt content
- Add BlockMeta for circleback, imap, and rss trigger integrations
- Add templates to clickhouse and greptile metas
- Remove duplicate PageSpeed deploy-gate template

* chore(telemetry): drop org_seat.drift_reconciled audit

System self-heal bookkeeping doesn't belong in the user-facing audit
trail — membership and seat-purchase changes are already audited, and
the cron's logger output covers ops visibility.

* chore(db): consolidate branch migrations into single 0227

* fix(emcn): make ChipModal scroll internally when content exceeds viewport

The Modal→ChipModal migration dropped the old ModalBody scroll container:
ChipModal renders ModalContent bare (no overflow-hidden) and its wrappers
had no min-h-0 chain, so tall modals (e.g. New data drain with an S3
destination) overflowed max-h-[84vh] off-screen with no way to scroll.

Complete the flex min-h-0 chain through ChipModal's frame and give
ChipModalBody flex-1 min-h-0 overflow-y-auto — header/footer stay pinned,
body scrolls only when constrained. Short modals are unaffected (max-h
caps, it doesn't stretch), and dropdowns inside the body are Radix-portaled
so the new scroll container cannot clip them. Five modals that had locally
patched this with max-h-[Nvh] overrides keep working unchanged.

* fix(emcn): don't close modal when dismissing a dropdown via outside click

Radix dispatches pointer-down-outside to every open dismissable layer at
once, so clicking outside an open dropdown/select inside a modal closed
both the dropdown and the modal in one jarring step. ModalContent now
prevents its own dismissal while a portaled popper layer is open — the
first outside click closes just the popper, the next one closes the
modal.

* docs(skills): de-duplicate and correct agent docs; canonical styling tokens; mirror sim-sandbox rule

* docs(skills): broaden boundary-raw-fetch scope note, sync cursor rule mirrors

* fix(emcn): harden modal popper guard and exempt caret-anchored dropdowns from body scroll

Adversarial review follow-ups to the ChipModal scroll + dismissal fixes:

- Popper guard now requires data-state="open" inside the popper wrapper,
  so a dropdown that is merely animating closed no longer swallows the
  next outside click on the modal (DropdownMenu has an exit animation
  that keeps its wrapper mounted briefly)
- Port the same guard to SModalContent for consistency
- custom-tool-modal: opt its body out of the chrome scroll container
  (flex-none + overflow-visible); the caret-anchored EnvVar/Tag
  autocomplete dropdowns are absolute-positioned inside the body and
  must spill past its bounds rather than clip against a scroll boundary

* refactor(billing): drop unnecessary useCallback wrappers from event handlers

* fix(data-drains): complete chip migration of destination forms and fill missing placeholders

Finishes the in-flight FormField → ChipModalField conversion for all
destination form specs and adds the placeholders that several inputs
never had (S3 bucket/region/access keys, Azure account key, Datadog API
key, webhook signing secret/bearer token) — the cause of the New Data
Drain modal showing placeholder-less inputs inconsistently.

* fix(emcn): broaden modal popper guard to onInteractOutside

Covers the focusOutside dismissal path too: when a popper's focus scope
unwinds on close, the transient focus shift could still dismiss the
modal (and simultaneous body pointer-events lock teardown could freeze
the page). Same data-state="open" scoping as the pointer guard.

* fix

* fix(emcn): harden modal outside interactions

* Remove migration 0227 to regenerate after merge

Drop the 0227_org_member_usage_limit migration, its snapshot, and journal
entry so it can be regenerated cleanly after merging improvement/platform.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Regenerate migration as 0228 after merging improvement/platform

drizzle-kit generate re-emits the org_member_usage_limit table, document
uploaded_by, and table_run_dispatches triggered_by_user_id changes on top of
improvement/platform's 0227 snapshot. Also collapse the admin.tsx emcn import
left multi-line after merge conflict resolution.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix pre-existing type errors surfaced by post-merge type-check

These were present on dev before the merge; the post-merge type-check made
them visible.

- workspace-vfs.ts: materializeEnvironment now emits full oauth integration
  objects (id, providerId, displayName, role) so the summary matches
  WorkspaceMdData['oauthIntegrations'] (consumer reads credentialId from id)
- workflows/utils.ts: narrow resolved workflow rows to non-null workspaceId
  (the inArray filter already guarantees it) so the resolved result type holds

Co-authored-by: Cursor <cursoragent@cursor.com>

* Slack trigger

* fix tool mark complete bugs

* change thinking icon to just be blimp

* fix tests

* MCP fixes

* Fix files

* refactor use chat

* multiple fixes

* Fix deploys

* fix billing, tracing, retry lifecycle issues

* remove stale generated migration

Drop migration 0228 and its metadata so it can be regenerated after merging staging.

Co-authored-by: Cursor <cursoragent@cursor.com>

* merge staging in

* fix sockets err classification

* sockets invite flow fix

* remove stale generated migration 0229 before staging merge

Co-authored-by: Cursor <cursoragent@cursor.com>

* regen migration

* Update bun lock

* Fix schema issues

* Fix lint

* Fix build

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: andres <k62hc5kjst@privaterelay.appleid.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com>
Co-authored-by: waleed <waleed@simstudio.ai>
2026-06-09 13:39:51 -07:00
WaleedandTheodore Li 540835a7a5 feat(integrations): add AWS AppConfig integration with tools, block, and docs (#4928)
* feat(integrations): add AWS AppConfig integration with tools, block, and docs

* fix(integrations): preserve latestVersionNumber 0 and tighten locationUri type in AppConfig

* fix(integrations): use valid IntegrationTag values in AppConfig BlockMeta

* feat(appconfig): add list_hosted_configuration_versions operation

* fix(appconfig): stringify configurationVersion in block params

* feat(appconfig): add full CRUD for applications, environments, and configuration profiles

Adds get/update/delete for applications, environments, and configuration
profiles, plus delete_hosted_configuration_version — 10 tools rounding out
the integration to management-grade CRUD completeness.

---------

Co-authored-by: Theodore Li <theo@sim.ai>
2026-06-09 13:17:40 -07:00
Theodore Li 76774795c6 feat(auth): dynamic signup/login ban lists via AWS AppConfig (#4911)
* feat(auth): dynamic signup/login ban lists via AWS AppConfig

- Move blocked-domain/allowlist/MX gating from env vars into AWS AppConfig (queried at runtime via the AppConfig Data SDK with a 30s in-process cache); env vars remain a fallback for self-hosted/OSS.
- Add a new bannedEmails denylist that blocks a specific address at both sign-in and sign-up.
- Generic, profile-agnostic AppConfig client so future config (feature flags) reuses the same plumbing; AppConfig client shares the same credential resolution as the S3 client.
- Defense in depth: authenticateApiKeyFromHeader now rejects keys belonging to banned users.

* fix(auth): scope bannedEmails to signup only; harden AppConfig cache

- Remove bannedEmails sign-in check: better-auth's admin plugin already blocks banned users at sign-in (session.create.before, all providers). bannedEmails is now a signup-only denylist via user.create.before, which also closed the OAuth/email-OTP sign-in bypass the bots flagged.
- AppConfig cache: track a 'loaded' flag so an empty/unseeded profile warms the cache instead of re-polling on every request; honor NextPollIntervalInSeconds to avoid throttling; dedupe concurrent cold fetches behind one in-flight poll to avoid racing the rotating session token.

* refactor(auth): drop bannedEmails; gate AppConfig on isHosted

- Remove the bannedEmails denylist entirely (better-auth banning + blockedSignupDomains cover the cases).
- Move isAppConfigEnabled into feature-flags.ts and gate it on isHosted, so AppConfig is hosted-only; self-hosted/OSS always uses the env-var fallback and never constructs the AWS client.

* fix(appconfig): preserve session token on parse error

Narrow the token-resetting catch to only the network calls. A JSON/parse failure no longer discards the already-rotated session token (the round trip succeeded), so the next poll reuses it instead of opening a new StartConfigurationSession.
2026-06-08 20:41:42 -04:00
Theodore Li 15df5115e1 chore(tables): own fractional-indexing in-house, drop runtime dep (#4900)
* chore(tables): own fractional-indexing in-house, drop runtime dep

* chore(tables): fully remove fractional-indexing dependency and differential test
2026-06-06 20:15:07 -04:00
0075ab9cf6 improvement(platform): remove tour, simplify sidebar/header, drop loading skeletons (#4354)
* improvement(platform): workspace UI/UX overhaul + integrations catalog

Rework the workspace around the AI-workspace model: a Mothership home, a
top-level Skills route, connected-credential and integration-detail pages,
and a polished sidebar/settings surface. Replace the notifications store
with a unified toast system (provider-level dismiss/pause, countdown ring).

Integrations & catalog:
- Add a BlockMeta layer (tags + catalog templates) scoped to catalog-visible
  integrations; every catalog integration carries >=7 grounded templates.
- Rework the taxonomy: each block declares category tools|blocks|triggers.
  3rd-party services are 'tools'; first-party primitives (postgres, mysql,
  knowledge, file, search, stt/tts, image/video generators, thinking, etc.)
  are 'blocks'. Versioned blocks follow the upgrade paradigm (old hidden,
  latest in toolbar/docs).
- Generate integrations.json + tool docs canonically from block configs.

Architecture & cleanup:
- Consolidate block data extraction behind a single latest-version strategy
  (getCanonicalBlocksByCategory; version-consistent getBlockMeta).
- Unify version-suffix handling in @sim/utils/string (stripVersionSuffix /
  isVersionedType, with tests); registry, generate-docs, tools/utils, and
  integrations all route through it.
- Repair latent broken barrels, remove dead code, fix BlockMeta-related type
  errors and 5 broken docs links.

Behavior-preserving for block execution and the toolbar's tool/block listing.

* refactor(platform): remove forms, templates, and creators features

Remove three standalone features and their supporting code:
- Forms: form-deployment pages, API routes, execution path, and docs.
- Templates: the template gallery (landing + workspace) and template APIs.
- Creators: creator-profile routes and contracts.

Add a super-user permissions module (lib/permissions/super-user) and an
organizations API contract; update the audit/db/testing packages, billing,
and the session/theme providers accordingly.

* test(workflows): update archiveWorkflow update count after forms removal

The forms feature was removed, dropping the form-table update from
archiveWorkflow. Update the stale assertion from 8 to 7 tx.update calls.

* upgrade

* improvement(knowledge): polish tag filter dropdowns (#4816)

* improvement(logs): object storage backed tracespans (#4787)

* improvement(logs): obj storage backed tracespans

* fix storage write context

* fix tests

* address comments

* address comments

* chore(db): remove migration 0219 to regenerate after staging merge

Drops the 0219_robust_shard SQL, its snapshot, and the journal entry so the
trace-spans/cost schema migration can be regenerated on top of the latest
staging migration chain (avoids a number collision with staging's migrations).

Co-authored-by: Cursor <cursoragent@cursor.com>

* improvement(billing): accurate per-member usage via shared ledger helper

Per-member/per-user usage in the org-member routes now adds the usage_log
ledger to the currentPeriodCost baseline (which is no longer incremented),
via a shared getOrgMemberLedgerByUser helper to avoid repeating the
subscription→period→ledger lookup across the admin and member-facing routes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* regen migrations

* update migration

* address comments

* more code cleanup

* incorrect type cast

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* improvement(providers): harden OpenAI-compatible providers + add tests (#4796)

* improvement(providers): harden OpenAI-compatible providers + add tests

* fix(vllm): let tool-loop errors propagate instead of returning silent partial success

* fix(litellm): force tool_choice 'none' on final structured-output call

The deferred final call used tool_choice 'auto', so the model could emit
another tool_calls round instead of the structured answer, leaving content
stale. Use 'none' (matching vLLM/Fireworks) on both the streaming and
non-streaming final calls so the model must return the structured response.

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

* fix(providers/ollama): drop tools from post-tool streaming call

Ollama ignores tool_choice (not in its supported fields), so vLLM/Fireworks'
tool_choice:'none' guard is a no-op here. Omit tools from the final streaming
payload instead so the summarization turn can't emit dropped tool calls.

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

* fix(litellm): spread payload into deferred final call so reasoning_effort carries over

The non-streaming deferred finalPayload hand-picked fields and dropped
reasoning_effort (and any future payload field), diverging from the streaming
path which spreads ...payload. Spread payload here too for consistency.

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

* chore(providers/ollama): restore enrichment TSDoc block

Keeps parity with sibling Chat Completions providers (cerebras/mistral/xai).

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

* docs(fireworks): restore TSDoc on utils helpers

Restore the TSDoc blocks on supportsNativeStructuredOutputs,
createReadableStreamFromOpenAIStream, and checkForForcedToolUsage —
TSDoc is the codebase documentation standard and should not have been
stripped.

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

* chore(litellm): remove inline rationale comments (codebase uses TSDoc)

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

* chore(providers/ollama): drop orphaned enrichment TSDoc

The block documented a function that now lives in trace-enrichment.ts, so it
documents nothing in this file.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* chore(copilot): deprecate mcp server (#4797)

* chore(copilot): deprecate mcp

* update error codes

* deprecate copilot api v1 route

* feat(integrations): hosted API keys for Findymail, Prospeo, and Wiza (#4777)

* feat(integrations): hosted API keys for Findymail, Prospeo, and Wiza

Add hosted-key support across all credit-consuming Findymail, Prospeo, and Wiza operations so Sim provides the key when a workspace has not brought its own. Register the three BYOK providers, consolidate Wiza's two-step reveal into a single polling wiza_individual_reveal op, and hide the API key field on hosted Sim for hosted operations.

* fix(integrations): harden Wiza reveal polling, soften enrichment getCost guards

Address Greptile + Cursor Bugbot review on #4777: return explicit failures from the Wiza individual_reveal poller instead of throwing (thrown errors were swallowed into a false queued success), short-circuit when the initial reveal is already terminal, tolerate transient 5xx/429 during polling, and return 0 (not throw) from Findymail getCost when the contacts/employees array is absent.

* chore(integrations): biome formatting after wiza merge resolution

* fix(wiza): type isTerminalReveal param structurally for next build typecheck

* feat(enrichments): add Findymail, Prospeo, Wiza to work-email waterfall

* feat(enrichments): add Wiza + Prospeo phone reveal to phone-number waterfall

* feat(enrichments): opportunistic identifiers + LinkedIn URL input across work-email & phone cascades

* fix(tables): reduce column header chevron size and fix sidebar shadow bleed (#4800)

* feat(slack): add install + privacy section to integration landing page (#4799)

* feat(slack): add install + privacy section to integration landing page

Adds a hand-authored, slug-keyed landing-content module (separate from the generated integrations.json so it survives regeneration) and renders an install walkthrough + privacy-policy link on integration pages when present. Also refreshes generated docs (data-enrichment entry, icon mappings, tool mdx).

* fix(landing): render privacy section independently, align CTA analytics label

* docs(landing): clarify the Slack install button is behind sign-in

* refactor(landing): bake integration landing content into generated json via docs-gen

Moves landing content (install walkthrough + privacy) out of a render-time augment and into the generation pipeline: generate-docs reads the pure-data content map and writes landingContent into integrations.json, so the page reads a single source (integration.landingContent). Canonical types live in integrations/data/types.ts.

* improvement(enrichments): align enrichments sidebar with design system (#4801)

* improvement(enrichments): align enrichments sidebar with design system

* fix(enrichments): consistent close button pattern and fix url link hover

* fix(misc): upgrade path change for new better-auth version, billing issue for workflow block agent usage (#4803)

* fix(misc): upgrade path change for new better-auth version, double-billing for workflow block agent usage

* fail loudly if stripe sub id missing

* fix(copilot): seq migration (#4804)

* chore(db): drop redundant idx_webhook_on_workflow_id_block_id index (#4809)

Removed because (workflow_id, block_id) is a left-prefix of idx_webhook_on_workflow_id_block_id_updated_at_desc, which fully covers it. The dropped index was non-unique and enforced no constraint.

* perf(copilot): read chat transcripts from copilot_messages (R+1 cutover) (#4808)

* perf(copilot): read chat transcripts from copilot_messages, not JSONB

Flip user-facing chat reads from the legacy copilot_chats.messages JSONB
array (5.7GB, 99% TOAST) to the normalized copilot_messages table via a
new loadCopilotChatMessages helper ordered by seq NULLS LAST, created_at,
id — the verified canonical order. Both chat-detail getters
(getAccessibleCopilotChat, getAccessibleCopilotChatWithMessages) now drop
the messages column from their metadata select (no more whole-array
detoast on every load) and assemble the transcript from the table after
authorization. This cascades to the copilot + mothership GET endpoints
and to resolveOrCreateChat's conversationHistory (the LLM payload).

The normalize/effective-transcript pipeline is source-agnostic
(copilot_messages.content == a JSONB array element), so transcripts are
byte-identical. Dual-write and the JSONB column stay in place as the
internal-logic source and fallback; removing JSONB writes is a later step.

Prod integrity verified before cutover: 0 messages missing, 0 NULL-seq,
0 dup keys/seq, 0 orphans, order-parity vs JSONB = 0 mismatches.

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

* test(copilot): cover auth-deny on a found row skips the messages query

Address PR review: exercise the `if (!authorized) return null` contract —
when the chat row exists but authorization fails, the getter returns null
and never issues the copilot_messages read.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(tables): right-align run/stop in embedded toolbar; workflow cells format like normal cells (#4806)

* fix(tables): right-align run/stop in the embedded table toolbar

Add a right-aligned `trailing` slot to ResourceOptionsBar and move the embedded
mothership table's run/stop control into it, so Filter + Sort stay left-aligned
and run/stop sits opposite on the right. No-op for the search-bearing consumers
(logs, resource list), which don't pass `trailing`.

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

* fix(tables): workflow-output cells format values like normal cells

Workflow-output columns short-circuited in resolveCellRender and rendered their
value as plain text, so a sim-resource URL / external URL / JSON / date produced
by a workflow never got the chip, favicon link, or typed formatting a normal
cell gets. Factor value formatting into a shared `resolveValueKind` helper used
by both the workflow-value branch and the plain-cell branch; the workflow branch
keeps the typewriter reveal for plain streaming text via a `typewriter` flag.

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

* fix(tables): detect resource/URL links on workflow output regardless of column type

Workflow output columns default to `json` (columnTypeForLeaf), so routing their
values through the type-based formatter (a) gated chip/URL promotion behind
`column.type === 'string'` — a URL produced by a json-typed output never became
a chip — and (b) JSON.stringify'd plain string values, adding quotes and losing
the typewriter reveal. Detect links (sim-resource chip / favicon URL) on the
value string directly for workflow outputs, falling back to the plain `value`
kind; plain cells keep the type-based formatting. Addresses Greptile P2 on #4806.

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

---------

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

* fix(icons): repair broken integration icon rendering (#4810)

* fix(icons): repair broken integration icon rendering

Two distinct bugs left integration icons broken on the /integrations page
(visible at 32-40px, hidden at the toolbar's 16px):

1. Corrupted SVG paths (Notion, Greptile, Granola, Calendly, Grafana, Bedrock):
   over-minified data dropped elliptical-arc flag digits (e.g. `A1 1 0 5.9 7`
   instead of `A1 1 0 0 0 5.9 7`); Granola's cubic stream was truncated. Browsers
   abort path parsing at the first invalid arc flag, so each rendered as a fragment
   or blank. Replaced with correct path data from canonical sources, preserving each
   icon's existing fill/gradient and bgColor.

2. Invisible glyph (Bright Data): its icon uses fill='currentColor' but bgColor was
   '#FFFFFF', and every surface forces text-white on the glyph - white-on-white.
   Changed bgColor to Bright Data's brand blue (#3d7ffc) so the white glyph reads,
   matching the white-glyph-on-brand-chip convention.

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

* fix(icons): restore Calendly dual-tone brand colors

Addresses review feedback: the previous fix replaced the broken Calendly icon
with a monochrome #006BFF path, dropping the cyan #0ae8f0 accent from the
original dual-tone mark. Restored the two-tone logo (blue + cyan) using clean,
valid path data, cropped to a tight square viewBox so it fills the chip.

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

* improvement(icons): enlarge icons, fix Zoom contrast and Quiver chip

- Zoom: glyph was blue-on-blue (#0B5CFF on #2D8CFF chip); switched to
  currentColor so it renders as a white glyph on the blue chip.
- Quiver: chip bgColor #000000 -> #FFFFFF to match the icon's near-white box,
  and enlarged the mark slightly (viewBox crop).
- Enlarged (tightened viewBox, verified no clipping): RevenueCat, Prospeo,
  Granola, Firecrawl, Enrich.so, and the AWS icons (RDS, DynamoDB, SQS,
  CloudFormation, Athena, CloudWatch, SES, Bedrock, S3).
- ZoomInfo left unchanged: it is a full red rounded-square logo that already
  fills its frame, so a crop would clip it.

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

* fix(icons): use Bright Data wordmark on white chip; repair Circleback

- Bright Data: replaced the flame glyph with the official two-tone 'bright data'
  wordmark (provided asset), centered in a symmetric viewBox. Reverted the chip
  bgColor from #3d7ffc to #FFFFFF since the blue wordmark is invisible on a blue
  chip (the wordmark is designed for a light background).
- Circleback: a minifier had rounded the pattern's image scale to scale(0),
  collapsing the embedded logo to zero size (invisible). Restored the correct
  scale (1/280 = 0.00357142857) so the C. mark renders.

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

* fix(docs): sync Quiver block color card to white chip

Reflects the Quiver bgColor change (#000000 -> #FFFFFF) in the docs block info card.

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

* improvement(icons): enlarge AWS/Cloudflare/Dagster icons, fully white Zoom

- Enlarged (tighter viewBox, render-verified, no clipping): Cloudflare, Dagster,
  and the red AWS icons AWS IAM, Identity Center, Secrets Manager, SES, STS.
  Identity Center was anomalously small (filled ~32% of its frame); the group is
  now sized consistently (~80% fill).
- Zoom: the camera lens triangle was still #0B5CFF (blue-on-blue); switched it to
  currentColor so the whole camera renders white on the blue chip.

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

* docs(wiza): consolidate individual reveal into a single operation

Merges the separate Start/Get Individual Reveal operations into one Individual
Reveal operation in the Wiza docs and integrations data (operationCount 5 -> 4).

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

* improvement(icons): size remaining AWS icons to match the set (~80% fill)

Bring RDS, DynamoDB, SQS, CloudFormation, Athena, CloudWatch and S3 up to the
same ~80% fill as the AWS IAM/Identity Center/Secrets Manager/SES/STS group, so
all AWS icons are visually consistent. Bedrock left as-is (already ~92% fill).

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

* fix(icons): use Bright Data flame mark, enlarge ZoomInfo

- Bright Data: the full 'bright data' wordmark was illegible at chip size.
  Replaced with just the flame-'i' brand mark (blue #4280f6 on the white chip),
  centered.
- ZoomInfo: cropped the viewBox toward the white 'Zi' so it's larger; the red
  rounded-square background still fills the chip.

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

* improvement(icons): enlarge CrowdStrike icon

The falcon mark sat small in its chip because the icon used a wide 768x500
viewBox (letterboxed in the square chip). Switched to a square viewBox centered
on the mark so it fills ~80%, consistent with the other icons.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(tables): serialize schema mutations to prevent parallel column clobber (#4812)

* Make workflow description nullable

* fix(tables): serialize schema mutations to prevent parallel column clobber

* fix(tables): load workflow outside schema lock; use DbOrTx for getTableById

* fix(tables): scale idle timeout in updateColumnType to avoid aborting large type changes

* fix(tables): skip stale remap types when workflowId changes concurrently

* fix(tables): scale idle timeout in updateColumnConstraints for large tables

* fix(wait): resume live/draft async waits and preserve cell context on chained waits (#4814)

* Make workflow description nullable

* fix(wait): resume live/draft async waits and preserve cell context on chained waits

* improvement(knowledge): polish tag filter dropdowns

* improvement(knowledge): soften filter section labels

* improvement(knowledge): soften list filter labels

* fix(security): harden SSO domain registration, webhook path isolation, and CSV export (#4813)

* fix(security): harden KB file access, SSO domain registration, webhook path isolation, env secrets, and CSV export

* fix(sso): scope domain conflict query with indexed lower(domain) filter

Address PR review: avoid a full-table scan on every SSO provider
registration by filtering candidate rows in SQL with
lower(domain) = <normalized>, keeping the in-memory ownership check.
Also tighten the normalizeSSODomain TSDoc.

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

* chore: condense env route security comments

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

* icons update

* chore(security): tighten inline comments in CSV export and KB file authorization

Condense verbose comment blocks to concise TSDoc/single-line form; no behavior change.

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

* fix(security): validate internal serve origin in KB file authorization

Replace the bypassable isInternalFileUrl substring check in resolveInternalKbKey
with an origin allow-list (base URL, internal API base URL, TRUSTED_ORIGINS).
A crafted external host whose path is /api/files/serve/<victim-key> no longer
resolves to the victim key. Relative same-origin URLs are unaffected.

* style(sso): use idiomatic sql lower() comparison for domain conflict query

Match the repo's prevailing `sql`lower(col) = value`` idiom for the
case-insensitive SSO domain conflict lookup.

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

* fix(security): align workspace env admin gate with hasWorkspaceAdminAccess

Use the same admin check the secrets UI uses (owner, admin permission, or
org-admin) so owners and org-admins are not wrongly denied their own decrypted
workspace secrets, while read-only members remain restricted to names only.

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

* fix(sso): rely on lower(domain) match for conflict detection, drop dead in-memory recheck

Address PR review: the SQL `lower(domain) = <normalized>` predicate already
excludes rows that the in-memory `normalizeSSODomain(...) === domain` recheck
claimed to catch, making that recheck dead/misleading code. Match on the
canonical lower-cased domain and filter purely by ownership. Malformed legacy
values (wildcards, schemes, ports) never match an email domain at sign-in, so
excluding them is not a gap. Test DB mock now applies the lower() predicate so
the casing-variant case is genuinely exercised.

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

* fix(security): scope webhook deploy path conflict to active webhooks

findConflictingWebhookPathOwner omitted the isActive filter that the
runtime dispatcher (findAllWebhooksForPath) applies, so an inactive but
non-archived webhook from another workflow (e.g. after undeploy or
failure auto-disable) would permanently block any new deployment on that
path even though it never receives deliveries. Align the guard with the
runtime isActive + archivedAt filter; the earliest-owner runtime check
remains the authoritative cross-tenant protection. Also trims verbose
TSDoc on the webhook path-isolation helpers.

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

* fix(security): exclude archived workflows from webhook deploy path conflict

findConflictingWebhookPathOwner now joins workflow and filters
isNull(workflow.archivedAt), matching the runtime dispatcher
(findAllWebhooksForPath). A webhook on an archived workflow can never
receive deliveries at runtime, so it must not block legitimate path reuse
with a 409.

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

* fix(security): anchor KB file ownership to earliest document in any state

A KB file's owner is now the earliest document referencing its key regardless of
state (active/archived/deleted/excluded); access is granted only when that owning
document is still active. Closes the residual where an attacker could plant an
active document to claim a file whose original document was archived or deleted.

* updated greptile icon

* revert(security): drop KB file authorization changes

Reverts the knowledge-base file-access work (origin-pinning / owner-pinning /
origin allow-list in verifyKBFileAccess) and its test. The other hardening fixes
(SSO domain registration, webhook path isolation, workspace env secrets, CSV
export) are unchanged. apps/sim/app/api/files/authorization.ts is restored to its
origin/staging baseline.

* fix(sso): treat caller's own user-scoped provider as owned during conflict check

Self-hosters often register SSO user-scoped via the CLI script (no
SSO_ORGANIZATION_ID). If they later enable organizations and reconfigure the
same domain org-scoped through the UI, the conflict check previously treated
their own user-scoped row as another tenant's and returned a misleading 409.
Recognize the caller's own user-scoped provider as owned so that migration is
allowed, while still blocking another user's or another org's domain.

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

* revert(security): remove workspace-env admin gate

Defer to a credential-based access model (separate change). Restores
GET /api/workspaces/[id]/environment to main behavior and removes the test.

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

* refactor(security): consolidate webhook path-collision check into one helper

Extract findConflictingWebhookPathOwner to lib/webhooks/utils.server.ts as
the single source of truth for cross-tenant path-collision detection, used by
both webhook creation paths (deploy sync and the manual /api/webhooks route).

This also repairs two latent issues in the manual route's previous inline
check, which queried with limit(1) and only webhook.archivedAt:
- limit(1) inspected one arbitrary row, so a same-workflow row could mask a
  foreign collision (false negative). The shared helper scans all matching
  rows.
- It omitted isActive/workflow.archivedAt, so inactive or archived-workflow
  webhooks (which never receive deliveries) permanently blocked path reuse.
  The helper mirrors the runtime dispatcher's filter.

Same-workflow webhook reuse for upsert is now a separate, explicit lookup.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(security): block private/reserved IPs for hosted 1Password Connect SSRF (#4818)

* fix(security): block private/reserved IPs for hosted 1Password Connect SSRF

* test(security): use real isPrivateOrReservedIP and cover IPv6 edge cases

* improvement(integrations): validate and expand devin, cursor, and greptile (#4820)

* improvement(integrations): validate and expand devin, cursor, and greptile

- devin: fix missing org_id path segment on all session endpoints, add 7 session sub-resource tools (list messages/attachments, get/append/replace tags, archive, terminate), pagination, and is_archived output
- cursor: add get_api_key_info, list_models, list_repositories tools
- greptile: align block and docs
- normalize array outputs to default [] and tighten types

* refactor(cursor): simplify list_repositories v2 array normalization

Collapse the redundant `?? []` + `Array.isArray` double-guard into a
single Array.isArray check, per PR review feedback.

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

* fix(devin): scope session-tag mapping to tag ops and normalize array tag inputs

- Only map sessionTags into the tools tags param for append/replace operations,
  preventing stale sessionTags state from clobbering create_session tags
- Fall back to a wired tags value when sessionTags is empty for tag operations
- Normalize tag inputs (string or wired string[]) via normalizeTags so array
  values from other blocks no longer throw on .split

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

* fix(cursor): restore base64 file data in legacy download_artifact metadata

The legacy CursorBlock exposes only content + metadata (no v2 file
output), so metadata.data was the only way legacy-block workflows could
access downloaded artifact bytes. Restore the base64 data field and
document it in the outputs/type instead of dropping it.

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

* fix(devin): coerce terminateArchive to archive flag for boolean-wired input

* docs(integrations): regenerate tool docs for new devin and cursor operations

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(search-replace): don't auto-navigate when content edits invalidate the active match (#4819)

* fix(search-replace): don't auto-navigate when content edits invalidate the active match

* fix(search-replace): clear afterReplaceIndexRef on apply failure and zero matches

* fix(search-replace): remove duplicate setActiveSearchTarget(null) on close

* fix(search-replace): move afterReplaceIndexRef write inside handleApply past the guard

* fix(search-replace): auto-navigate when hydration resolves with no prior active match

* chore(search-replace): remove inline comments

* fix(search-replace): revert !activeMatchId guard that caused immediate re-navigation after deselect

* improvement(enrichments): limit company-info to fields both providers return (#4817)

Hunter's company dataset returns null industry/foundedYear for many large companies (verified against the live API for Microsoft, Amazon, Google), so under the first-non-empty-wins cascade those columns appeared inconsistently across rows. Limit company-info outputs to employee count and description — the fields Hunter and PDL both reliably return — so every row is consistent. employeeCount is a string so Hunter's range bucket and PDL's exact count share the column.

* fix(files): don't reject external URLs containing '..' in file parse validation (#4821)

* fix(files): don't reject external URLs containing '..' in file parse validation

The file block's file_fetch operation rejected any external URL whose path
contained '..' (e.g. Slack files-pri slugs with a literal '...') with
'Access denied: path traversal detected'. Traversal checks only apply to
local paths — external http(s) URLs are fetched with SSRF protection
downstream and are never resolved against the filesystem, so they now
short-circuit as valid. Internal /api/files/serve/ URLs keep full traversal
protection.

* test(files): fix external-URL assertion to handle undefined error

* test(files): assert success explicitly in external-URL traversal test

* fix(files): keep traversal protection for https URLs matching internal serve paths

* feat(google-sheets): add row filtering to read with numeric operators (#4822)

* feat(google-sheets): add row filtering to read with numeric operators

Adds client-side row filtering to the Google Sheets read (v2) operation.
Filter the returned rows by a header column using text operators
(contains, not_contains, exact, not_equals, starts_with, ends_with) and
numeric/ordering operators (gt, gte, lt, lte). Filtering lives in a pure,
unit-tested helper (filterSheetRows) and runs over the fetched read range;
an optional `filter` output reports whether the column was found and how
many rows matched.

Also hardens the surrounding tools:
- trim spreadsheetId in write/update/append URL builders (matches read)
- URL-encode the v1 read default range
- expose valueInputOption for the update operation in the block

Backwards compatible: with no filter requested, read output is byte-
identical and the `filter` field is omitted. The filterMatchType union is
widened additively (4 -> 10 values).

* fix(google-sheets): correct filter metadata for missing column and header-only sheets

- matchedRows is now 0 (not totalRows) when the filter column is not found,
  so it no longer contradicts applied=false / columnFound=false
- columnFound now reflects an actual header lookup for empty/header-only
  sheets instead of being hardcoded true
- add tests covering header-only and empty sheets with present/absent columns

* fix(selectors): fetch all pages for paginated dropdown list routes (#4823)

* fix(selectors): fetch all pages for paginated dropdown list routes

Dropdown selectors fetched only the first page of paginated provider
APIs, silently hiding results past page one. Add bounded server-side
draining to the list routes across Microsoft Graph, Google, Notion,
Atlassian, Linear, AWS CloudWatch, and offset/token REST APIs, plus a
shared client-side drain cap in the selector hook. Response shapes,
stored values, and tool execution are unchanged; CloudWatch list tools
still honor a caller-supplied limit. Also fixes the Word file picker
that was searching for .xlsx files.

* fix(selectors): harden JSM and Monday pagination draining

- JSM service-desk/request-type drains advance `start` by the actual row
  count returned (not the fixed page size) and stop on an empty page, so a
  short non-final page can't skip items.
- Monday boards drain now checks `response.ok` per page, surfacing a
  mid-drain HTTP failure instead of treating it as an empty final page and
  returning a partial 200.

* docs(selectors): clarify JSM drain advances start by actual row count

The offset-advancement fix (advance `start` by the rows returned, not the
fixed page size) landed in 7b19788a8; update the TSDoc to match so it no
longer reads as advancing by `limit`.

* fix(selectors): drain fetchPage in direct fetchList callers

Making `fetchList` optional left three direct callers (outside the
useSelectorOptions hook) calling it unguarded, which broke the build's
type check. Route them through a shared `loadAllSelectorOptions` helper
that uses `fetchList` when present and otherwise drains `fetchPage`.
This also prevents a regression: `confluence.spaces` / `knowledge.documents`
now paginate via `fetchPage` only, and these callers (search/replace,
value resolution) would otherwise have silently returned no options.

* chore(selectors): rename MAX_PAGE_PAGES to MAX_NOTION_PAGES for readability

* fix(sso): re-check domain conflict before write and reject IP-address domains (#4825)

* improvement(copilot): make copilot_messages the sole transcript store, remove JSONB dual-write (#4826)

Stop writing/reading the legacy copilot_chats.messages JSONB column now that
reads are cut over to copilot_messages. Make appendCopilotChatMessages the
primary write (throws on failure instead of swallowing), repoint peripheral
readers (workspace VFS, chat cleanup, data drains, fork, superuser import) to
copilot_messages, and persist the assistant turn inside finalizeAssistantTurn's
transaction so it commits atomically with the stream-marker clear. The column
itself is dropped in a follow-up migration after this bakes.

* feat(tables): expand filter operators (not-contains, starts/ends-with, not-in, empty) (#4827)

Add does-not-contain ($ncontains), starts-with ($startsWith), ends-with
($endsWith), not-in-array ($nin, previously executed server-side but unexposed
in the UI), and is-empty/is-not-empty ($empty) filter operators end-to-end —
SQL builder, condition types, query-builder converters/constants, the filter
UI, the Table tools/block descriptions, and docs.

Also fix correctness bugs in the filter builder surfaced by the wider operator
set:
- Same-column AND rules (e.g. age > 18 AND age < 65, or name startsWith 'A'
  AND name endsWith 'Z') silently overwrote each other because the AND group
  was keyed by column name. They now merge into one operator object, which
  also makes Filter -> rules -> Filter round-trip losslessly for multi-operator
  columns.
- $nin values were not split into an array like $in, and textual-match values
  like "123" were numeric-coerced (breaking the ILIKE path).
- A non-boolean $empty operand from the raw API silently inverted the check; it
  now coerces 'true'/'false' strings and otherwise returns a 400.

* improvement(copilot): stop persisting tool-call result outputs in transcripts (#4829)

Opening a Mothership task could take many seconds because a single persisted
assistant message in copilot_messages.content can reach hundreds of MB, almost
entirely inside contentBlocks[].toolCall.result.output (e.g. a get_workflow_logs
or run_workflow result). The DB query is ~2ms; the cost is detoasting that
payload, shipping it to the browser, and parsing it.

These outputs are dead weight on the Sim side: they are never rendered (the
thread shows only tool name/title/status) and never replayed to the model (the
upstream copilot service owns conversation memory). So drop result.output before
it is persisted, keeping result.success/error plus the tool metadata.

- add stripToolResultOutput() in persisted-message.ts
- apply it in messages-store toRow (covers every write path) and in
  loadCopilotChatMessages (existing rows render fast on read)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* feat(providers): add Together AI, Baseten, and Ollama Cloud model providers (#4830)

* feat(providers): add Together AI, Baseten, and Ollama Cloud model providers

* fix(providers): guard Ollama streaming fast-path with hasActiveTools

Match Together/Baseten/Fireworks: when tools are supplied but all are
filtered out (usageControl 'none'), take the single streaming call instead
of an extra non-streaming round-trip.

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

* fix(providers): filter non-chat model types from Together model list

* refactor(providers): dedupe Ollama Cloud upstream schema

ollamaCloudUpstreamResponseSchema was byte-for-byte identical to
ollamaUpstreamResponseSchema (both /api/tags endpoints return the same
{ models: [{ name }] } shape). Drop the duplicate and reuse the shared schema.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(knowledge): calendar view sync, deduplicate popover animation classes, type-safe filter cast

* cleanup(knowledge): remove TRIGGER_BORDER_CLASS duplication, inline displayLabel, drop enabledFilterParam alias

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com>

* feat(blocks): add BlockMeta to Quiver and Linq; fix invalid block config fields; update skills

Block fixes:
- Add QuiverBlockMeta (tags + 3 templates: icon generator, diagram creator, vectorizer)
- Fix QuiverBlock: remove invalid tags field from BlockConfig, IntegrationType.Design →
  IntegrationType.AI (Design doesn't exist in the enum)
- Fix GreptileBlock: remove invalid tags field from BlockConfig,
  IntegrationType.DeveloperTools → IntegrationType.DevOps
- Fix LinqBlock: remove invalid tags field from BlockConfig (tags belong only in BlockMeta)

Skills:
- add-block: add dedicated BlockMeta section with structure, rules, and registration
  pattern; add BlockMeta checklist items
- add-integration: add BlockMeta to block structure template, add rules clarifying
  that tags must NOT appear on BlockConfig and integrationType must be a valid enum
  value; update registry snippet to include blocksMeta; add checklist items

* fix(integrations): fix category dropdown by defining missing LANDING_INTEGRATIONS_DATA_PATH and regenerating integrations.json

The staging merge introduced landing-content.ts but forgot to define
LANDING_INTEGRATIONS_DATA_PATH in generate-docs.ts, causing the script
to crash before writing integrations.json.

The stale JSON had integrationTypes (plural array) from an older script
version, while the Integration type and workspace UI both read
integrationType (singular string) — so ALL_CATEGORY_SECTIONS bucketed
to undefined and the category filters never appeared in the dropdown.

Fixed by adding the missing path constant and re-running the generator.
integrations.json now has 192 entries with the correct integrationType field.

* fix(sidebar): restore resize handle on all pages

commit 3109104582 wrapped the resize handle in {(isCollapsed ||
isOnWorkflowPage) && ...} and added a useEffect that resets sidebar
width to SIDEBAR_WIDTH.MIN whenever the user navigates away from a
workflow page. Together these made the sidebar non-resizable on Tasks,
Tables, Knowledge Base, and every other non-workflow page.

Restore the staging behavior: always render the resize handle and
remove the effect that forced the width reset on page transitions.

* fix(sidebar): match staging onKeyDown and tabIndex on resize handle

The resize handle was still conditionalizing onKeyDown and tabIndex
on isCollapsed, blocking keyboard accessibility of the separator role
when expanded. Staging always attaches both unconditionally.

onKeyDown={isCollapsed ? handleEdgeKeyDown : undefined} → onKeyDown={handleEdgeKeyDown}
tabIndex={isCollapsed ? 0 : undefined}                 → tabIndex={0}

* feat(integrations): show connected credentials on integration detail page

When navigating to /integrations/google-docs (or any integration), a
Connected section now appears above the templates listing all workspace
credentials tied to that provider. Each row links back to the credential
detail page (/integrations/connected/${id}) for management actions.

Pairs with the earlier change that routes connected items from the
integrations list to the provider detail page instead of directly to
the credential detail page.

* fix(integrations): rename Add in chat to Add to Sim

* fix(skills): rename Add button to Add to Sim

* fix(platform): restore M1/M2/M3 regressions and LazyMotion on landing page

M1 — Invitation guard: re-introduce usePermissionConfig().isInvitationsDisabled
alongside the workspace inviteDisabledReason check. The flag now also
respects NEXT_PUBLIC_DISABLE_INVITATIONS and EE permission-group
disableInvitations, not just billing policy.

M2 — Settings redirects: /settings/integrations and /settings/skills
now server-redirect to /integrations and /skills respectively so old
bookmarks and emails don't silently land on General.

M3 — Starter block search exclusion: restore block.type !== 'starter'
guard in the search store so users cannot add a duplicate Starter block
via the command palette.

LazyMotion: restore LazyMotion + domMax/domAnimation wrappers and m.*
components in landing-preview-panel and landing-preview-home. The
removal was accidental (the full motion bundle was left after an
import cleanup), which caused the entire framer-motion feature set to
load eagerly on the landing page.

* fix(integrations): revert connected list to credential detail; remove settings redirects

* feat(sidebar): restore workspace switcher search with updated styling

Shows a search input in the workspace dropdown when the user has more
than 3 workspaces (WORKSPACE_SEARCH_THRESHOLD). Keyboard navigation:
ArrowDown/Up to move through results, Enter to switch, resets on close.

Styled to match the current branch (border-1/surface-5 tokens, sm text,
11px Search icon) rather than the old staging styles. Highlight state
is wired through chipVariants active prop so it follows the same active
appearance as clicked/hovered items.

* fix(sidebar): clean up workspace search — layout, memo, and effect guard

* fix(sidebar): align workspace rename input selection style with workflow rename

* perf(sidebar): eliminate React re-renders during sidebar drag resize

Previously, every mousemove during resize called setSidebarWidth(), which
both updated the --sidebar-width CSS variable (sync) and set Zustand state
(async). This caused:
  1. A 1-frame transition flash on mousedown — isResizing state had to
     round-trip through React before the is-resizing CSS class was applied,
     so the width transition fired for the first pixel of movement.
  2. A React re-render per pixel dragged — components reading sidebarWidth
     from the store (avatars, usage-indicator) lagged one frame behind the
     container, making the + and ... buttons appear to jump ahead.

New approach:
  - handleMouseDown adds is-resizing directly to the sidebar DOM node before
    any React involvement (synchronous, no frame lag).
  - mousemove writes only to the CSS custom property (zero React renders).
  - mouseup persists the final width to Zustand exactly once.
  - isResizing / setIsResizing state removed from the store and hook — they
    are no longer needed since the class is managed via direct DOM mutation.

* perf(sidebar): add requestAnimationFrame throttle to resize mousemove handler

* fix(sidebar): fix drag-right lag caused by WorkspaceChrome overflow-hidden transition

The sidebar-container's is-resizing class correctly suppressed its own width
transition, but the two wrapper divs in WorkspaceChrome both have
transition-[width]/transition-transform with a 175ms ease. The outer wrapper
also has overflow-hidden, so while the sidebar content was at the correct width
instantly, it was visually clipped by the outer wrapper which was still
animating — causing the + and ... buttons to appear to lag behind the resize
line on drag-right (not on drag-left, since shrinking doesn't clip content).

Fix: add sidebar-shell-outer/sidebar-shell-inner class names to both chrome
wrappers, and suppress their transitions via html.sidebar-resizing rule when
a drag is active. The html.sidebar-resizing class is toggled directly in the
resize hook alongside is-resizing, so it takes effect synchronously on mousedown.

* fix(icons): redesign Download icon to match Upload style; fix Upload/Download confusion

Download icon was missing the tray/shelf line at the bottom that Upload has,
making it look like a plain arrow rather than a matched pair. Updated Download
to use the same viewBox, stroke weight, and three-path structure as Upload
(tray + stem + arrowhead), just pointing down.

Also fix 5 places where Upload (↑) was incorrectly used for download/export
actions:
  - files.tsx: two Download action rows in toolbar and context menu
  - tables/table.tsx: Export CSV toolbar button
  - table-context-menu.tsx: Export CSV context menu item
  - logs.tsx: Export toolbar button
  - landing-preview-logs.tsx: decorative Export button

Import CSV and actual upload actions correctly keep the Upload icon.

* fix(icons): replace Upload with Download on all remaining export/download actions

- panel.tsx: Export workflow dropdown item
- context-menu.tsx: Export in sidebar workflow context menu
- chat.tsx: Export chat button
- output-panel.tsx: Export console CSV button
- terminal.tsx: Export console CSV button
- resource-content.tsx: Export table as CSV + Download file buttons

* fix(icons): fix remaining Upload→Download on download actions in files and logs

- action-bar.tsx: download button in files toolbar
- file-row-context-menu.tsx: Download item in file context menu
- file-download.tsx: both download buttons in log details file viewer

* updated block skills, settings pages, modals, buttons -> chips, blocks missing metadata

* updated skill modal

* improvement(resource-header): refine breadcrumb truncation ux

* improvement(resource): add floating overflow text tooltips

* wire up credits counter

* improvement(resource-header): mute path dropdown title

* refactor(resource-header): share floating-tooltip engine, prune dead overlay tooltips (#4844)

Clean up the breadcrumb truncation feature for reuse and correctness:

- Extract useFloatingTooltip / useIsOverflowing / FloatingTooltip into a shared
  floating-tooltip module. BreadcrumbSegment and FloatingOverflowText now consume
  one implementation instead of duplicating ~150 lines of positioning, velocity,
  overflow-detection, and portal logic.
- Replace the hardcoded terminal-label regex in ResourceHeader with a typed
  `terminal` flag on BreadcrumbItem (set by the document chunk/loading crumbs),
  decoupling the generic header from knowledge-base copy.
- Clear the path-popover close timeout on unmount and reuse the shared
  POPOVER_ANIMATION_CLASSES constant.
- Drop the redundant manual overflow-state writes (fixes a sticky fade mask).
- Revert FloatingOverflowText inside Combobox `overlayContent` back to plain
  truncating spans across files/logs/tables/scheduled-tasks/document: the combobox
  overlay is pointer-events-none, so the tooltip handlers never fired there.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(emcn,resource-header): address PR #4844 review feedback

- useIsOverflowing now uses a callback ref so the ResizeObserver follows the
  element across mount/unmount/reassignment instead of capturing it once at mount.
  Safe for conditionally rendered consumers of the shared hook. (greptile P2)
- Move POPOVER_ANIMATION_CLASSES out of chip-date-picker implementation internals
  into emcn/components/popover/popover-animation.ts, exported from the
  @/components/emcn barrel. Consumers now import from the module boundary.
  (greptile P2)

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

* upgrade table and styling upgrade

* fix schema to include integration

* fix(files): align delete icon with tables view (Trash → Trash2)

Co-Authored-By: waleed <waleed@simstudio.ai>

* fix(mothership): preserve blockType for integration contexts in sent messages

Integration mention chips were missing their provider icons in sent messages
because blockType was dropped when mapping ChatContext to messageContexts.
renderIntegrationTile returns null without blockType, silently hiding the icon.

* fix(mothership): allow 'integration' resource type in chat resources API

The VALID_RESOURCE_TYPES allowlist was missing 'integration', causing a
400 error when adding integrations to the Mothership resource tab — so
they never persisted and disappeared on refresh.

* fix(ui): Add "File" title next to file resource header

* fix(ui): fix resource header columns being bolded

* fix(resource): keep the floating tooltip from jumping on click

Gate the focus-driven show behind :focus-visible so a mouse click (which
focuses the trigger) no longer re-shows the tooltip anchored to the element's
bottom edge. On click the tooltip now hides cleanly instead of jumping down;
keyboard focus still shows it.

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

* perf(sidebar): eliminate unnecessary re-renders in workspace switcher for non-search users

- onMouseEnter: only set highlightedIndex when showSearch is true, preventing
  a state update + re-render on every workspace row hover for users with ≤ 3
  workspaces where the search is never shown
- onOpenChange: only reset workspaceSearch and highlightedIndex when showSearch
  is true, since both values are always already at their defaults for non-search
  users and setting them triggers a pointless re-render during dropdown close
- data-workspace-row-idx: only set when showSearch is true since the scroll
  effect that reads this attribute is already gated on showSearch

* feat(search): context-aware cmd-k results on the integrations page

When cmd-k is opened on the integrations page, show two new result
groups: connected accounts (visible even with empty input) and catalog
integrations (appear once the user types). Selecting an OAuth integration
deep-links to its detail page with ?connect=oauth so the connect modal
auto-opens. Non-OAuth integrations navigate to the plain detail page.

Both groups are gated to the integrations page only and respect the
hideIntegrationsTab permission. The credentials fetch shares the same
React Query cache key as the integrations page itself (no double fetch).

* refactor(emcn): make the floating tooltip the one canonical Tooltip

Replace the Radix-based emcn Tooltip with the cursor-following floating tooltip so
every tooltip in the app uses one consistent style. Built on the shared
floating-tooltip engine (relocated into emcn), not a parallel implementation.

- Move the floating-tooltip engine into emcn/components/tooltip and export it from
  the barrel; re-point its consumers (FloatingOverflowText, resource-header)
- Extend the FloatingTooltip bubble to render arbitrary children (+ role/id for
  a11y) so it can back general tooltips, not just overflow text
- Rebuild emcn Tooltip (Root/Trigger/Content/Provider/Shortcut/Preview) on
  useFloatingTooltip — compound API preserved, ~350 call sites unchanged, legacy
  side/align props accepted and ignored (the tooltip follows the cursor). Removes
  @radix-ui/react-tooltip usage (package kept for a later cleanup; react-slot
  retained for asChild)

Note: general tooltips now show instantly (no hover delay) and follow the cursor.

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

* style(emcn): put tooltip text on the design scale (text-caption)

Replace the tooltip's ad-hoc `text-xs` + `leading-[18px]` with the semantic
`text-caption` (12px) font-size token so the text styling is fully on the design
scale and self-documenting, matching how the rest of the system is set up. The
color already used the global `--text-body` token. No visual change (still 12px
with a ~18px line height).

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

* feat(sidebar): add empty task state and inline task creation

- Show "No tasks yet" in the Tasks section (expanded and collapsed) when the list is empty
- Clicking + now creates a task via the API and navigates directly to it, rather than navigating to home
- Add isCreatingTaskRef guard to prevent double-click from spawning multiple tasks
- Disable + button while creation is pending
- Fall back to home navigation on creation error

* invite, billing, home

* improvement(seats): auto purchase seats on invitations into workspace (#4857)

* improvement(seats): auto purchase seats on invitations into workspace

* improve sampling for seat drift reconciler

* address comments

* feat(knowledge): align connector UI with integrations page styling

- ConnectorTypeCard now matches integration rows: brand-colored rounded-xl tile, ArrowRight, title/subtitle hierarchy
- ConnectorCard icon upgraded from flat surface-4 to branded tile (white icon on brand bg, graceful fallback)
- Connector header badges use chipVariants instead of custom Button classes
- Add-connector search input aligned to integrations style (h-[30px], rounded-lg, border-1)

* fix(icons): trim Folder SVG viewBox to remove right-side whitespace

The folder path only extends to x≈14.33 in a 15-unit viewBox, leaving
~0.5 units of empty space on the right. At 12px rendered size this
produces ~0.4px extra gap (visible as ~1px on retina displays) compared
to solid icons like the workflow color square. Trimming the viewBox to
14.5 units makes the folder fill its chip slot evenly.

* fix(user-input): restore draft text synchronously to preserve contexts on nav

The SSR-safe approach (empty useState + effect restore) created a timing
window where the sync effect in useContextManagement fired with message=''
before the value was set, clearing any restored contexts. Folder and workflow
contexts (not re-added by applyAutoMentions) were lost on every nav-back.

Revert to the staging approach: initialize value synchronously from the
draft store so message is already populated when effects run, matching
the behavior on staging.

* fix(queue): render context chips in queued messages

- Remove plainMentions from queued message rows so context chips render
  with icons, consistent with sent messages
- Fix computeMentionRanges to use '/' prefix for skill contexts (content
  has the slash trigger restored at submit time, not '@')

* fix(mothership): remove integrations from add-resource dropdown

* fix(mothership): comment out integrations from add-resource dropdown

* chore(db): drop form, templates, template_creators, template_stars tables

These tables backed the Forms and Templates platform features which were
intentionally removed from this branch. Clean up the DB schema to match.

* chore(db): add migration metadata for 0224 drop tables

* block icons, sidebar, toolbar

* chore: remove remaining dead code for template-profile feature

- Remove 'template-profile' from SettingsSection union type
- Remove 'template-profile' entry from SECTION_TITLES
- Remove now-redundant template-profile guard in settings sidebar
- Remove commented-out template-profile nav item

* fix(multi-select): preserve anchor on range selection for tasks and folders

After a shift+click range, the anchor (lastSelectedTaskId / lastSelectedFolderId)
was being updated to the end of the range (toId). This caused subsequent
shift+clicks to extend from the wrong point instead of the original click.

Standard behavior: anchor stays at the initial click (fromId) so repeated
shift+clicks always expand/contract relative to where you started.

* feat(emcn): add SearchInput component and unify search bars platform-wide

- Add SearchInput to emcn: 30px chip-family filled search input matching the
  integrations page pattern (border-1, surface-5, leading Search icon)
- Migrate all 22 search bars across settings, EE pages, and integrations to
  SearchInput (only layout classes allowed at callsites)
- Rename Sim Keys -> Sim API Keys in nav/title; page copy now says API key
- Remove components/ui input, label, and verified-badge; migrate consumers
  to emcn equivalents or raw inputs (table cell editor, wand prompt bar)
- Delete dead EE skeleton files (data-drains, data-retention)
- General settings: Home Page chip moves to header left as navigation

* fix(files,tables): restore new-file editor autofocus and CSV import error toasts

Both were dropped on the staging line and regressed vs production (main):

- Files: the new-file editor autofocus chain (files.tsx -> file-viewer ->
  text-editor) was stripped by the react-doctor dead-code pass in #4544,
  which misread the prop-drilled `autoFocus` (consumed by an imperative
  `editor.focus()` effect) as unused. Restored the prop through all three
  layers and the one-shot focus effect so creating a new file focuses the
  editor immediately.
- Tables: CSV import failures were silently logged with no user feedback.
  Restored the per-file and generic `toast.error` surfacing.

* feat(home): score suggested actions by workspace signals

- Derive the suggestion pool from the curated block template catalog
  (1,343 prompts across 172 blocks) instead of 15 hardcoded entries
- Fix inverted relevance: prompts for connected providers are now boosted
  4x (instantly runnable) instead of excluded; unconnected discounted 0.4x
- Weight by featured (3x), popular category (1.5x), and resource gaps
  (no tables -> boost table starters; has KBs -> dampen KB-creation prompts)
- Weighted sampling without replacement, max one suggestion per block
- Connect rows weighted by catalog template count; 2 for fresh workspaces,
  1 once something is connected
- Key the catalog map by both versioned and base block types so gmail_v2
  templates resolve (gmail, github, notion, linear were silently dropped)
- Replace derive-in-effect state with a useMemo keyed by a shuffle nonce
- Add suggested_action_clicked / suggested_actions_shuffled /
  suggested_actions_toggled PostHog events

* billing, teammates

* improvement(credentials): credentials invites, secrets tab wiring up (#4874)

* improvement(credentials): move away from invite notion

* wire up secrets ui/ux

* address comments

* get consistent styling by removing emcninput + text area

* styling consistency

* remove fallback

* address comment:

* refactor(ui): migrate settings & workspace UI to chip design system

Migrate modals to ChipModal (showDivider, hint, resizable, size, leading,
ChipModalTabs), standardize Chip variants, add ChipCombobox wrapper, and apply
chip inputs/dropdowns across settings, knowledge, logs, tables, inbox, EE tabs.
Render ChipModalTabs as a ChipSwitch segmented control. Align /settings/secrets
detail with /integrations, refresh whitelabeling, and restore file-editor
autofocus and CSV import error toasts.

* fix(mothership): restore integrations to useAvailableResources for @ mention

Integrations were fully removed from useAvailableResources which broke
the @ mention menu since user-input shares the same hook. Now integrations
are always included in the hook but excluded at the AddResourceDropdown
component level, keeping them out of the sidebar + menu while remaining
available for @ mention autocomplete.

* refactor(settings): chip design-system consistency pass across all tabs

Extract a shared chip-field shell (CHIP_FIELD_SHELL/CHIP_FIELD_INPUT) mirroring
Input variant='chip' and route secrets, credential detail, and integrations
credential detail through it (30px height, font-medium, focus ring). Add a
Discard action to the secrets header when dirty. Group BYOK providers into
Models/Search & web/Enrichment sections and align its tiles to the integrations
tile.

Normalize list-row typography to text-[14px]/text-[12px] and icon tiles to
rounded-xl + border across api-keys, copilot, custom-tools, mcp,
workflow-mcp-servers, credential-sets, and access-control. Tone the secrets
Details chip and per-row affordances to ghost. Fix token correctness: raw
tailwind colors to design tokens (data-drains), missing chip variant on the
Snowflake role input, ColorInput chip-field reuse (whitelabeling), error token
and icon sizes (workflow-mcp-servers), border token (mcp), no-results sizing
(secrets), row chrome (recently-deleted), hover token and Button to Chip
(access-control), and deduped textarea chrome (sso).

* feat(settings): unify filter dropdowns on ChipSelect (integrations style)

Add a ChipSelect emcn component — a filled chip trigger + chevron opening a
DropdownMenu, matching the integrations category filter — supporting single
select, multi-select (checkbox rows), grouped options, and optional in-menu
search. Migrate every settings/EE filter dropdown off ChipCombobox to it:
audit-logs (resource-type multi + time-range), data-retention, data-drains,
general, admin (grouped tool picker), inbox status filter, and the workflow
MCP-server pickers. The SSO provider-id field stays an editable combobox since
it accepts free-text slugs.

Also fix audit findings: chip the MCP client-secret input, normalize an MCP
error-text size, and drop now-dead destination-icon code in data-drains.

* fix(mentions): require explicit @ for integration mentions; decorate sent messages robustly

- Bare integration names in prose (Monday, Notion, Clay) are no longer
  auto-converted to mentions or chipped — mention treatment is strictly
  opt-in via a token-starting @ (fixes the scunthorpe problem)
- @-prefixed mentions still canonicalize casing (@slack -> @Slack) on both
  the keystroke fast-path and bulk paths (paste, template, draft, STT)
- Sent/queued messages now self-sufficiently decorate @IntegrationName
  tokens via a text scan, covering messages sent before the input pass
  ran or authored outside the chat input
- Integration contexts missing a resolvable blockType (messages persisted
  before blockType was saved) are backfilled by label lookup so their
  mention pills render the brand icon again

* refactor(settings): section the API keys page like secrets

Wrap Workspace, Personal, and the allow-personal-keys toggle in SettingsSection
(muted label + divider) instead of bare bold headers, matching the secrets and
BYOK pages.

* fix(settings): ChipSelect renders above modals + full-width form mode

Raise the ChipSelect menu to --z-popover so it layers above modal surfaces
(--z-modal) instead of opening behind them. Add a fullWidth prop that stretches
the trigger and right-aligns the chevron for form-field use, and apply it to the
workflow MCP-server pickers.

* fix(emcn): ChipSelect uses the emcn flat chevron, not lucide's square one

The lucide ChevronDown is square; rendering it at the chip's 9x7 footprint
stretched it. Switch to the custom emcn ChevronDown (built for that wide aspect),
matching the integrations filter and ChipDropdown.

* fix(emcn): ChipSelect trigger hugs its content (w-fit)

In a stacked form layout the trigger was stretched by align-items: stretch,
leaving an empty gap to the right of the value. Add w-fit so the chip sizes to
its content (a compact pill) everywhere; fullWidth form selects are unaffected.

* fix(emcn): ChipSelect uses a square lucide chevron

Revert to lucide's ChevronDown sized square (size-[14px]) so it renders crisp,
matching the standard select chevron used by Combobox.

* renamed tasks to chats

* rename and file change

* improvement(billing): wire up billing, org, teammates tabs + remove deprecated subscription tab (#4887)

* improvement(billing): wire up billing, org, teammates tabs + remove depr subscription tab

* pass exec timeout to tool routes

* reuse helper

* address comments

* address disable comment

* chore(db): remove migration 0224 to regenerate on top of staging

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix type errors and regen migration?

* chore(db): drop branch migration 0226 ahead of staging merge; will regenerate

* chore(db): regenerate migration 0226 after staging merge

* externalize before compaction in fallback'

* fix save/discard chips to be consistent

* fix(ui): remove smodal tabs in favor of chip modal tabs

* fix(ui): skip auto-scrolling on mouse highlight of workspace

* fix(platform): restore settings redirects, forgot-password Enter submit, and tag tooltip visibility

- Re-add SETTINGS_REDIRECTS so /settings/integrations and /settings/skills
  deep links redirect to their top-level routes instead of rendering an
  empty settings panel (accidentally removed in 86da193cc3 one minute
  after cca5054cf6 added it)
- Add opt-in onSubmit to ChipModalField input/email variants and wire it
  in the forgot-password modal so Enter submits again (lost in the
  ChipModal conversion)
- Knowledge tag tooltip: drop the max-h/overflow-y-auto clamp that the
  pointer-events-none floating tooltip made unreachable; truncate each
  tag row instead so all tags stay visible with bounded height

* chore(db): remove migration 0226_third_spot before staging merge

* chore(db): regenerate migration as 0227 after staging merge

* feat(telemetry): add posthog + audit coverage for new platform actions

Audit log (compliance/permission-relevant only):
- org_seat.provisioned — seat auto-purchased when an invite acceptance
  grows the org (actor = accepting user, includes seat delta)
- org_plan.converted — Pro→Team conversion triggered by invite acceptance
- org_seat.drift_reconciled — hourly cron healed a drifted seat count
- credential_member.added/removed/role_changed — credential sharing
  surface was previously fully unaudited
- table.created — parity with existing table.updated/deleted
- skill updates now record skill.updated instead of mislabeled
  skill.created

PostHog:
- seats_provisioned, credential_shared/unshared,
  environment_updated/deleted (key counts only, never names/values)
- table_import_started/completed — background CSV imports previously had
  zero failure observability
- table_exported, file_downloaded, skill_updated
- credential_connected now fires for OAuth completions (draft-hooks),
  credential_deleted for OAuth disconnects; previously only manual
  credentials were tracked
- table_workflow_run gains deployment_mode (live/deployed/mixed)

Also: logger.warn on all new credential-admin 403 denials (members +
environment routes); invite-created audit enriched with
enforcedFixedSeats/plan.

Deliberately excluded as noise: per-hour dead-letter audit rows (would
re-record the same stuck event every cron run) and a duplicate
system-actor org_plan.converted in the Stripe outbox handler.

* feat(home): fill textarea on suggested prompt click instead of sending

Clicking a prompt action in the Suggested Actions panel now populates
the Mothership user-input textarea (via applyAutoMentions) and focuses
it with the caret at end, rather than immediately submitting. The user
can review, edit, and send manually.

* fix(templates): name owning integration in featured block template prompts

Four featured prompts omitted their owning integration name, making them
unbranded and disconnected from their title. Each prompt now explicitly
names GitHub or Google Sheets so mentionifyIntegrations renders the chip
and the copy reads as a self-contained agent-building instruction.

* fix(templates): rewrite fragment prompts so each names its integration and reads as a complete instruction

Featured and non-featured block template prompts that either omitted
the owning integration's canonical name or were phrased as marketing
fragments rather than natural user instructions have been rewritten.
Each updated prompt now starts with an imperative verb ("Build a
workflow that…"), names the owning integration explicitly so the
@-mention chip renders correctly, and aligns with the entry's title.

Files changed: salesforce, hubspot (×2), github, slack, airtable,
firecrawl, iam.

* fix(templates): name integration in remaining block template prompts

- google_docs.ts: replace 'Google Doc' with 'Google Docs document' in 4 prompts; update title 'Meeting notes to Google Doc' to 'Meeting notes to Google Docs'
- google_sheets.ts: replace 'Google Sheet' with 'Google Sheets spreadsheet/Google Sheets' in 3 non-featured prompts
- slack.ts: replace 'Google Doc' with 'Google Docs document' in 'Daily standup summary'
- stripe.ts: replace 'Google Sheet'/'Slacks' with 'Google Sheets'/'Slack' in 'Weekly metrics report'
- reddit.ts: add 'Reddit' to 3 prompts that only referenced subreddits
- notion.ts: rewrite featured prompt to start with a verb and name Notion
- jira.ts: rewrite featured marketing-fragment prompt to start with a verb
- linear.ts: rewrite featured marketing-fragment prompt to start with a verb
- gmail.ts: rewrite featured marketing-fragment prompt to start with a verb and name Gmail

* fix(icons): convert monochrome dark brand icons to currentColor for dark mode

LinkupIcon, InfisicalIcon, IntercomIcon, LumaIcon, GranolaIcon, OnePasswordIcon,
and RailwayIcon were hardcoded to black/near-black fills/strokes, making them
invisible in bare DARK mode. Convert all to currentColor so they follow the
theme-aware text color.

Add iconColor: '#286efa' to IntercomBlock (Intercom brand blue is a confident
mid-tone, safe on both themes).

Two-tone icons (StagehandIcon, AgentPhoneIcon, QuiverIcon) are left unchanged
because their white details are structural — converting to single-tone would
destroy logo legibility.

* removed color, user input, sidebar, suggested actions, chips/emcn

* removed color migration

* improve(blocks): audit block catalog metadata for accuracy and fill gaps

- Fix template prompts that claimed capabilities blocks don't have
  (fabricated triggers, non-existent tools) across ~98 integrations
- Normalize integration tags to family conventions and valid union values
- Align alsoIntegrations and modules with template prompt content
- Add BlockMeta for circleback, imap, and rss trigger integrations
- Add templates to clickhouse and greptile metas
- Remove duplicate PageSpeed deploy-gate template

* chore(telemetry): drop org_seat.drift_reconciled audit

System self-heal bookkeeping doesn't belong in the user-facing audit
trail — membership and seat-purchase changes are already audited, and
the cron's logger output covers ops visibility.

* chore(db): consolidate branch migrations into single 0227

* fix(emcn): make ChipModal scroll internally when content exceeds viewport

The Modal→ChipModal migration dropped the old ModalBody scroll container:
ChipModal renders ModalContent bare (no overflow-hidden) and its wrappers
had no min-h-0 chain, so tall modals (e.g. New data drain with an S3
destination) overflowed max-h-[84vh] off-screen with no way to scroll.

Complete the flex min-h-0 chain through ChipModal's frame and give
ChipModalBody flex-1 min-h-0 overflow-y-auto — header/footer stay pinned,
body scrolls only when constrained. Short modals are unaffected (max-h
caps, it doesn't stretch), and dropdowns inside the body are Radix-portaled
so the new scroll container cannot clip them. Five modals that had locally
patched this with max-h-[Nvh] overrides keep working unchanged.

* fix(emcn): don't close modal when dismissing a dropdown via outside click

Radix dispatches pointer-down-outside to every open dismissable layer at
once, so clicking outside an open dropdown/select inside a modal closed
both the dropdown and the modal in one jarring step. ModalContent now
prevents its own dismissal while a portaled popper layer is open — the
first outside click closes just the popper, the next one closes the
modal.

* docs(skills): de-duplicate and correct agent docs; canonical styling tokens; mirror sim-sandbox rule

* docs(skills): broaden boundary-raw-fetch scope note, sync cursor rule mirrors

* fix(emcn): harden modal popper guard and exempt caret-anchored dropdowns from body scroll

Adversarial review follow-ups to the ChipModal scroll + dismissal fixes:

- Popper guard now requires data-state="open" inside the popper wrapper,
  so a dropdown that is merely animating closed no longer swallows the
  next outside click on the modal (DropdownMenu has an exit animation
  that keeps its wrapper mounted briefly)
- Port the same guard to SModalContent for consistency
- custom-tool-modal: opt its body out of the chrome scroll container
  (flex-none + overflow-visible); the caret-anchored EnvVar/Tag
  autocomplete dropdowns are absolute-positioned inside the body and
  must spill past its bounds rather than clip against a scroll boundary

* refactor(billing): drop unnecessary useCallback wrappers from event handlers

* fix(data-drains): complete chip migration of destination forms and fill missing placeholders

Finishes the in-flight FormField → ChipModalField conversion for all
destination form specs and adds the placeholders that several inputs
never had (S3 bucket/region/access keys, Azure account key, Datadog API
key, webhook signing secret/bearer token) — the cause of the New Data
Drain modal showing placeholder-less inputs inconsistently.

* fix(emcn): broaden modal popper guard to onInteractOutside

Covers the focusOutside dismissal path too: when a popper's focus scope
unwinds on close, the transient focus shift could still dismiss the
modal (and simultaneous body pointer-events lock teardown could freeze
the page). Same data-state="open" scoping as the pointer guard.

* fix(emcn): harden modal outside interactions

* update audit mock

---------

Co-authored-by: andres <k62hc5kjst@privaterelay.appleid.com>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: waleed <waleed@simstudio.ai>
2026-06-06 11:39:48 -07:00
Theodore Li 24a6086d95 feat(tables): fractional order keys for O(log n) row insert/delete (flag-gated, default off) (#4890)
* feat(tables): add order_key column, fractional-indexing util, and ordering flag (off)

* feat(tables): write order_key on insert, flag-gate delete reindex + query ordering, add backfill

Flag off (default) = identical behavior. Single-insert assigns a fractional
order_key; queryRows orders by order_key when the flag is on; deletes skip the
O(N) reindex when on. Per-table-atomic backfill script populates existing rows.

* feat(tables): write order_key on all insert paths (batch, upsert, replace, import, create, copilot)

Completes the always-write-keys prerequisite: every row insert now assigns a
fractional order_key consistent with position order, so the flag can be flipped
safely after backfill. Flag off (default) still = identical behavior.

* feat(tables): insert-by-neighbor-id + orderKey on wire + client order-by-key

Inserts express intent as afterRowId/beforeRowId (O(1) key mint via the
(table_id,order_key,id) index); orderKey is returned on every row; client
reconcile/undo place by orderKey (no neighbor bump) with position fallback.
Flag off = unchanged. 205 table tests pass.

* feat(tables): resolve position-based inserts by key ordinal under the flag

Position-based callers (mothership tool, v1 API, undo fallback, transient old
clients) resolve their insert neighbor by order_key ordinal (OFFSET) when the
flag is on — positions are gappy then, so WHERE position=N would miss. Flag off
keeps the indexed position lookup. The mothership tool itself is unchanged.

* test(tables): flag-on coverage — delete skips reindex, insert mints key + no shift

* fix lint

* chore(db): regenerate order_key migration with default drizzle name

* fix(tables): address review — guard neighbor insert + mutual-exclusion + safe reconcile

- resolveInsertByNeighbor throws when the anchor row is missing (was silently
  inserting at the front) and when its order_key is null under the flag.
- insert contract: afterRowId/beforeRowId are mutually exclusive (refine).
- reconcileCreatedRow only key-sorts when every cached row is keyed, so mid-
  backfill un-keyed rows aren't yanked to the front.

* fix(kb): restore non-null guard in storage-key filter (unsafe-lint regression)

* refactor(tables): extract maxOrderKey + thread import append key

- Extract maxOrderKey(executor, tableId) helper; replaces three identical
  max(order_key) selects (single/batch insert append + import).
- Import: read the append anchor once up front and thread each batch's last
  key forward (nextImportStartOrderKey + afterOrderKey) instead of re-scanning
  max(order_key) per batch over a growing table — one scan per import, not
  one per 1k-row batch.

* fix(tables): keep insert body base omittable for v1 contract

The afterRowId/beforeRowId mutual-exclusion .refine() turned the schema into a
ZodEffects, which Zod forbids .omit() on — v1's insertTableRowBodySchema.omit({
position }) threw at module load (runtime-only; tsc misses it). Split the plain
object base out, apply the shared refine on top, and have v1 omit from the base
then re-apply it.

* fix(tables): chunk backfill order-key writes

A single UPDATE … FROM (VALUES …) over a whole large table overflows the JS call
stack while drizzle assembles the VALUES list (and would blow past Postgres's
65535 bound-param limit at ~32k rows) — large tables failed with 'Maximum call
stack size exceeded'. Write in 1000-row chunks inside the same per-table
transaction so keying stays atomic.

* fix(tables): emit orderKey in insert responses

The single-row and batch insert handlers dropped orderKey from the JSON
response even though the service returns it, so reconcileCreatedRow always fell
back to position-sorting and could place neighbor inserts wrong under the
fractional-ordering flag. Serialize orderKey alongside position.

* fix(tables): restore by orderKey, not position, under fractional flag

A saved position is the gappy column value, but under the flag insert reads
position as a visual rank (OFFSET) — so position-based restore misplaces rows.

- create-row redo now goes through the batch path carrying the saved orderKey
  (the single-insert API has no orderKey field); drop the now-unused single
  create mutation.
- resolveBatchInsertOrderKeys appends under the flag instead of feeding gappy
  positions to resolveInsertOrderKey; positions remain the flag-off path.

* perf(tables): backfill writes 5000 rows/chunk (was 1000)

5x fewer round-trips per table; ~10k bound params stays well under Postgres's
65535 ceiling and far below the single-statement size that overflows the stack.

* fix(tables): drop rowNumber from table trigger payload

position is gappy under the fractional-ordering flag, so rowNumber (= row.position)
no longer reflects a contiguous visual rank. Rather than compute-on-read, remove
it from the trigger payload, output schema, and column-execution input.

Also pin isTablesFractionalOrderingEnabled=false in update-row.test.ts so its
flag-off position-shift assertions are deterministic regardless of local env.

* chore(db): format generated 0226 migration metadata

biome check . flagged the drizzle-generated _journal.json and 0226_snapshot.json;
apply the formatter so packages/db lint:check passes in CI.

* docs(triggers): drop rowNumber from table trigger outputs

rowNumber was removed from the table trigger payload; remove it from the
documented output fields to match.

* test(tables): remove flag-on fractional-ordering unit suite

Flag-on behavior is covered by manual large-table verification; the heavily-
mocked DB-chain suite added little signal.
2026-06-05 17:18:33 -04:00
Theodore Li 530b2c0082 feat(metrics): emit hosted-key metrics to Grafana via OTel (#4885)
* feat(metrics): emit hosted-key metrics to Grafana via OTel

Replace the dropped platform.hosted_key.* spans with OTel counters/histograms for usage, cost, failures, throttles, and queue waits. Wire a MeterProvider into the Next.js OTel SDK (trigger.dev already exports metrics). Per-key attribution via a key label (env var name).

* fix(metrics): correct hosted-key failure attribution

- Re-point used/cost/failed labels at the freshly acquired key after reacquire
- Classify quota-style 401/403 as rate_limited (mirror isRateLimitError)
- Count returned success:false runs (e.g. deep_research polling) as failed

* fix(metrics): label hosted_key.throttled with real provider on exhausted retries

* fix(metrics): parse OTLP metrics URL via URL/pathname, not string suffix

Handles query strings and trailing slashes so the /v1/traces->/v1/metrics
swap can't produce a malformed endpoint, matching normalizeOtlpTracesUrl.
2026-06-04 18:14:39 -04:00
Theodore LiandClaude Opus 4.8 3518b999af feat(tables): background import for large CSVs with live progress (#4861)
* feat(tables): background import for large CSVs with live progress

* fix(tables): address review — import heartbeat, overlap guard, column/empty validation

* fix(tables): guard sync import overlap, scope fileKey to workspace, delete-on-replace after download

* fix(tables): stream large CSV imports from storage instead of buffering the whole file

* test(tables): fix async-import route tests for workspace-scoped fileKey + name uniquification

* fix(tables): append imports start after existing rows; reconcile missed import failures in the tray

* fix(tables): delete the uploaded CSV from storage after the import finishes

* fix(tables): validate replace before deleting rows; ignore stale replayed import events by importId

* fix(tables): bind import worker to its importId (no stale-worker clobber/overlap) and destroy storage stream on failure

* feat(tables): byte-based import progress, cancel support, and a start toast that opens the import view

* fix(tables): don't emit ready after cancel; honor cancel during the upload phase

* improvement(tables): use a stop (square) icon for canceling an active import

* fix(tables): make markTableImporting an atomic claim to close the concurrent-import TOCTOU race

* improvement(tables): preview CSV import from a slice, drop client row-count warning

The import dialog parsed the entire file in the browser to show an exact row
count and a row-limit warning. That holds the whole file in memory, blocks the
main thread, and hits V8's ~512MB string ceiling — so the dialog capped the
effective import size well below what the streaming importer handles.

Parse only the first 512KB (headers + sample for the mapping); drop the exact
count and the "would exceed the row limit by N" gate. The DB row-count trigger
already enforces max_rows server-side, so an over-limit import fails fast during
the run with a clear message instead of being blocked by an expensive parse.

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

* fix(tables): gate import ownership every batch and stop canceled imports reappearing

- Worker checked run ownership only at the progress cadence (~every 5k rows), so
  a canceled/superseded import could insert several more batches (incl. the final
  partial batch) before stopping. Move the updateImportProgress ownership gate to
  the top of every flush — a run that lost the table stops within one batch.
- A list/dialog import canceled mid-upload left the server row `importing` until
  the in-flight server cancel landed; hydration re-seeded it from useTablesList,
  so the dismissed import flickered back. Flag the real table id canceled on the
  mid-upload cancel path, skip re-seeding flagged tables in hydration, and clear
  the flag once the server import is terminal.

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

* refactor(tables): drive import tray by polling derived from server, not SSE

Import progress no longer holds an SSE connection per importing table. The tray
now derives its importing rows live from the table list (React Query), polled
only while an import is in flight; the table detail page keeps its own
cell-state SSE for grid refresh.

- store holds only client-only state now: optimistic uploads, which terminal
  completions to surface this session, canceled ids, menu open — no copied
  importStatus/rowsProcessed.
- useWorkspaceImports is the single source: polls via a data-predicate
  refetchInterval, derives rows, and fires completion toasts on the
  importing -> terminal transition.
- kickoff handlers use startUpload/setUploadPercent/endUpload; the invalidated
  list refetch surfaces the server row and polling takes over.
- removes use-hydrate-import-tray + use-import-progress-tracker (folded in).
- trims over-verbose comments across the import paths.

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

* fix(tables): ignore superseded-run import events in the detail SSE cache

applyImport applied every replayed import payload to the detail cache. The SSE
buffer can replay a prior import's terminal event for the same table, stomping a
newer in-flight import's UI. Lock to the active run's importId (and ignore a
replayed terminal before the id is known), matching the guard the header tracker
used to have.

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

* fix(tables): close sync-import TOCTOU by claiming the atomic import gate

The sync import route checked importStatus from a checkAccess snapshot, then
parsed/validated/wrote seconds later without taking the atomic claim. A
concurrent async kickoff (markTableImporting) could slip into that window and
both writers would run together — for replace mode, two delete+insert passes
leave the table indeterminate.

Claim the same atomic gate (markTableImporting) right before the write and
release it in the finally (before the response returns, so a client refetch
never sees the transient status). A row-level FOR UPDATE was avoided on purpose:
it would invert lock order against the position advisory lock / row-count
trigger and risk a deadlock — markTableImporting is the established gate.

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

* fix(multipart): keep abort wired after resolve so a mid-upload disconnect tears down the stream

readMultipart resolves on the file-part header and hands the caller an un-drained
stream, but settle() ran cleanup() and detached the abort listener on that path
too. A client disconnect mid-upload then destroyed nothing — busboy never saw EOF,
the file stream stalled, and the route's `for await` held a request slot until
maxDuration (300s). Re-arm an abort handler scoped to the file stream on resolve,
detached when the stream closes.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 21:26:06 -04:00
Waleed a7b0bd311d fix(deps): upgrade vitest to ^4.1.0 to patch critical Vitest UI advisory (GHSA-5xrq-8626-4rwp) (#4837)
* fix(deps): upgrade vitest to ^4.1.0 to patch critical Vitest UI advisory (GHSA-5xrq-8626-4rwp)

- Bump vitest and @vitest/coverage-v8 to ^4.1.0 across all workspaces (only patched release for the critical 'Vitest UI server arbitrary file read/execute' advisory; no 3.x backport exists)
- Widen @sim/testing peer range to ^3.0.0 || ^4.0.0
- Migrate constructor mocks to class expressions: vitest 4 uses Reflect.construct for mocks invoked with new, and arrow/function implementations are not constructable (function expressions also get reverted to arrows by biome's useArrowFunction)
- Remove deprecated test.poolOptions from apps/sim/vitest.config.ts (options are now top-level in vitest 4)

* fix(deps): exclude vulnerable vitest 4.0.x from @sim/testing peer range

Tighten the v4 arm of the peer range to >=4.1.0 <5.0.0 so the peer
requirement cannot be satisfied by the unpatched 4.0.x builds that
GHSA-5xrq-8626-4rwp affects.

* fix(testing): make vitest 4 constructor mocks type-check cleanly

- logging-session & mcp-oauth mocks: a class passed to mockImplementation has
  a construct signature that isn't assignable to its (...args) => any parameter,
  failing tsc. Use named function declarations instead (constructable via
  Reflect.construct, assignable to mockImplementation, and not rewritten to
  arrows by biome's useArrowFunction).
- database.mock.ts: vitest 4's generic vi.fn typings no longer break the
  self-referential cycle on the transaction callback's tx param; loosen tx and
  annotate the callback's return type to resolve the implicit-any errors.

* test(isolated-vm): de-flake queue-capacity scheduler tests

The 'queue is full' and 'per-owner queued limit' tests relied on
'await sleep(1)' to assume the first request had reached the queue before
submitting the overflow request. The first request only enqueues after an
async spawn-failure chain (acquireWorker -> spawn exit -> resolve null ->
enqueue), which isn't guaranteed within 1ms under CI load — the overflow
request then found an empty queue and hit the 200ms queue-wait timeout
instead of the capacity rejection.

Replace the wall-clock barrier with a deterministic, event-driven one: hold
the single global concurrency slot (IVM_MAX_CONCURRENT=1) with an active
worker and await an explicit 'dispatched' signal (fired when the worker
receives its execute message, after the scheduler counts it active). The
follow-up requests then deterministically hit the synchronous enqueue path.
Also drops the queue-wait timeout from 200ms to 50ms, so the tests run faster.
2026-06-01 16:11:35 -07:00
WaleedandClaude Opus 4.8 20a8d85e15 improvement(integrations): validate and expand devin, cursor, and greptile (#4820)
* improvement(integrations): validate and expand devin, cursor, and greptile

- devin: fix missing org_id path segment on all session endpoints, add 7 session sub-resource tools (list messages/attachments, get/append/replace tags, archive, terminate), pagination, and is_archived output
- cursor: add get_api_key_info, list_models, list_repositories tools
- greptile: align block and docs
- normalize array outputs to default [] and tighten types

* refactor(cursor): simplify list_repositories v2 array normalization

Collapse the redundant `?? []` + `Array.isArray` double-guard into a
single Array.isArray check, per PR review feedback.

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

* fix(devin): scope session-tag mapping to tag ops and normalize array tag inputs

- Only map sessionTags into the tools tags param for append/replace operations,
  preventing stale sessionTags state from clobbering create_session tags
- Fall back to a wired tags value when sessionTags is empty for tag operations
- Normalize tag inputs (string or wired string[]) via normalizeTags so array
  values from other blocks no longer throw on .split

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

* fix(cursor): restore base64 file data in legacy download_artifact metadata

The legacy CursorBlock exposes only content + metadata (no v2 file
output), so metadata.data was the only way legacy-block workflows could
access downloaded artifact bytes. Restore the base64 data field and
document it in the outputs/type instead of dropping it.

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

* fix(devin): coerce terminateArchive to archive flag for boolean-wired input

* docs(integrations): regenerate tool docs for new devin and cursor operations

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 18:53:29 -07:00
Vikhyath MondretiandCursor 49673055be improvement(logs): object storage backed tracespans (#4787)
* improvement(logs): obj storage backed tracespans

* fix storage write context

* fix tests

* address comments

* address comments

* chore(db): remove migration 0219 to regenerate after staging merge

Drops the 0219_robust_shard SQL, its snapshot, and the journal entry so the
trace-spans/cost schema migration can be regenerated on top of the latest
staging migration chain (avoids a number collision with staging's migrations).

Co-authored-by: Cursor <cursoragent@cursor.com>

* improvement(billing): accurate per-member usage via shared ledger helper

Per-member/per-user usage in the org-member routes now adds the usage_log
ledger to the currentPeriodCost baseline (which is no longer incremented),
via a shared getOrgMemberLedgerByUser helper to avoid repeating the
subscription→period→ledger lookup across the admin and member-facing routes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* regen migrations

* update migration

* address comments

* more code cleanup

* incorrect type cast

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 13:47:32 -07:00
Waleed 34c47f7e93 chore(auth): upgrade better-auth 1.3.12 → 1.6.11 (#4766)
* chore(auth): upgrade better-auth 1.3.12 → 1.6.11

* chore(auth): address Greptile review — broaden change-email type + migration newline

* fix(auth): correct oneTimeToken expiresIn unit (minutes, not seconds)

Better-auth's oneTimeToken expiresIn is in minutes (multiplied by 60_000ms
internally). Sim's existing 24*60*60 evaluated to ~60 days of token
lifetime instead of the intended 24 hours. Tokens are one-time-use and
typically consumed within seconds of generation (Socket.IO handshake),
so this tightens an unused security window without affecting UX.
2026-05-28 11:25:28 -07:00
Vikhyath MondretiandCursor 81bcdf255a improvement(schedules): retries, concurrency limits (#4755)
* improvement(schedu
les): retries, concurrency limits

* improvement(schedules): remove stale generated migration

Drop the pre-merge generated 0213 migration so it can be regenerated after syncing with staging.

Co-authored-by: Cursor <cursoragent@cursor.com>

* address comments

* fix pinned version for lru cache

* retryable errs cleanup

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 11:31:49 -07:00
Waleed 5d1aa228f5 fix(files): attach wheel listener before paint and guard SVG src (#4744)
* fix(files): attach wheel listener before paint and guard SVG src

* chore(deps): upgrade turbo to 2.9.14
2026-05-26 14:14:49 -07:00
Vikhyath Mondreti 1af65381f5 improvement(search-replace): pass down to subblocks (#4712)
* improvement(search-replace): pass down to subblocks:

* fix local lowercasing bug
2026-05-21 19:16:18 -07:00
Theodore LiandClaude Opus 4.7 4002242d84 feat(tables): virtualize data grid with bounded copy and chunked delete (#4693)
* feat(tables): virtualize data grid with bounded copy and chunked delete

* fix(tables): keyboard scroll past sticky header, copy toast row count, clipboard error handling

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

* fix(tables): keep copy/cut progress toast until the operation completes

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

* fix(tables): stop bulk cut chunks on first failure, reconcile grid on error

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:17:20 -04:00
Waleed cf146931dd improvement(memory): replace unbounded server caches with lru-cache to fix heap growth (#4652)
* fix(memory): prune toolSchemaCache and semaphores to prevent heap growth

toolSchemaCache (lib/copilot/chat/payload.ts): module-level Map keyed by
userId:workspaceId never deleted expired entries, only checked TTL on read.
With 100K+ unique user/workspace pairs each holding 50-200KB of tool schemas,
this was the primary driver of the 24MB -> 25GB heap growth observed in
CloudWatch. Add a setInterval sweep every 30s (matching the TTL) with .unref()
so it does not prevent graceful shutdown.

semaphores (lib/core/async-jobs/backends/database.ts): acquireSlot created
Semaphore entries that releaseSlot never deleted. With per-execution UUID keys
(e.g. scheduleJobId), each scheduled workflow run would add a permanent entry.
Store the concurrency limit on the Semaphore struct and delete the entry from
the Map when all slots are free and no waiters remain.

validatorCache (lib/copilot/tools/server/generated-schema.ts): validated as
bounded (93 tools x 2 schema kinds = 186 max entries, ~2-9MB). No fix needed.

isolated-vm nativeContexts: validated as deferred GC, self-healed by worker
rotation at MAX_EXECUTIONS_PER_WORKER=200. externalMB spikes trace to
concurrent isolate heaps at peak load (128MB limit x active isolates), not a
reference leak. No fix needed.

* fix(memory): prune effectiveEnvCache and instrument cache sizes in telemetry

effectiveEnvCache (lib/environment/utils.ts): same unbounded accumulation
pattern as toolSchemaCache — module-level Map keyed by userId:workspaceId
with a 15s TTL that is only checked on read, never proactively evicted.
Adds a periodic sweep matching the TTL interval with .unref().

cache-registry (lib/monitoring/cache-registry.ts): lightweight registry
so modules can expose their cache sizes to telemetry without coupling.
toolSchemaCache and effectiveEnvCache both register on module load.

memory-telemetry: emits cacheSizes in every Memory snapshot log so
CloudWatch can confirm the caches stay bounded post-deploy.

* improvement(memory): replace manual TTL Maps with lru-cache for toolSchemaCache and effectiveEnvCache

Replaces the homegrown Map + setInterval sweep pattern with LRUCache from
the lru-cache npm package, which is the standard Node.js solution for
bounded in-process caching with TTL.

Changes per cache:
- Removes manual ToolSchemaCacheEntry / EffectiveEnvCacheEntry types
- Removes setInterval sweep timers (and the .unref() boilerplate)
- Removes the two-phase promise->value entry update inside the IIFE
- Stores Promise<T> directly — in-flight and resolved states share one type
- max: 200 (toolSchemaCache) / max: 500 (effectiveEnvCache) as hard ceilings
- TTL behaviour and concurrent-request deduplication are preserved exactly
- cache-registry .size reporting works unchanged via lru-cache's .size prop

* fix(memory): remove redundant waiters guard in releaseSlot
2026-05-18 11:58:25 -07:00
WaleedandClaude Opus 4.7 08eeecbebe fix(security): KB fileUrl LFI, MCP/Agiloft SSRF pinning, form OTP, KB authz (#4639)
* fix(security): KB fileUrl LFI, MCP/Agiloft SSRF pinning, form OTP, KB authz

* fix(otp): don't leak caught error.message; fail-closed on DB retry exhaust

- Chat/form OTP routes: replace `error.message || fallback` with generic
  `Failed to process request` in 500 responses (logger still captures detail).
- otp.ts incrementOTPAttempts DB path: on MAX_RETRIES exhaustion, delete the
  verification row and return `'locked'` instead of trusting a possibly-
  undercounted final read.

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

* fix(mcp): use undici fetch directly in pinned-fetch for typed dispatcher

Replace `globalThis.fetch` + double-cast with `undici.fetch` so the
`dispatcher` option is part of the real type contract. This guarantees
pinning won't silently break if a future runtime swaps the underlying
fetch implementation.

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

* fix(build): keep agiloft/grafana tool configs client-safe

Tool config files are statically reachable from the client bundle (via
tools/registry.ts → tools/{service}/index.ts). Importing
`@/lib/core/security/input-validation.server` from these files pulled
`node:dns/promises` into the Turbopack client bundle and broke the build.

Split agiloft utils into client-safe (`utils.ts`, plain fetch + sync
`validateExternalUrl`) and server-only (`utils.server.ts`, DNS-pinned
variants). Routes that need TOCTOU protection import the pinned helpers;
the executor-side tool path falls back to sync URL validation (matches
the supabase precedent and pre-PR baseline).

Grafana update tools likewise switch from `secureFetchWithValidation`
(server-only) to inline sync `validateExternalUrl` + plain fetch.

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

* fix(knowledge): case-insensitive scheme checks for fileUrl

Boundary schema accepted uppercase schemes (e.g. HTTPS://, DATA:) via the
case-insensitive http regex, but the processor's case-sensitive
startsWith('data:') / startsWith('http') / startsWith('https://') checks
rejected them with a confusing "Unsupported fileUrl scheme" error.
Aligns processor checks to the schema using case-insensitive regex per
RFC 3986 §3.1.

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

* fix(mcp): annotate undici/DOM type-bridge double-casts in pinned-fetch

Strict audit was failing on two new `as unknown as` casts in pinned-fetch.ts.
They bridge DOM `RequestInit`/`Response` ↔ undici equivalents (structurally
compatible at runtime since Node's global fetch is undici) and are required
to satisfy the FetchLike contract. Annotate so they count as documented
exemptions instead of new violations.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 23:05:52 -07:00
WaleedandCursor a64b338236 fix(files): fixed resource spacing on files directories pages (#4618)
* fix(files): fixed resource spacing on files directories pages

* fix(build): align turbo schema version

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-15 12:48:15 -07:00
Waleed 9bbbe0adcb chore(deps): bump mermaid to 11.15.0 for GHSA-ghcm-xqfw-q4vr (#4615)
* chore(deps): bump mermaid to 11.15.0 for GHSA-ghcm-xqfw-q4vr

* chore(deps): override transitive mermaid to 11.15.0
2026-05-15 10:42:42 -07:00
Waleed 11fa96cac0 chore(deps): bump next to 16.2.5 for CVE-2026-44578 SSRF fix (#4606)
* chore(deps): bump next to 16.2.5 for CVE-2026-44578 SSRF fix

* chore(deps): bump next to 16.2.6 for full May 2026 security release coverage
2026-05-14 14:34:05 -07:00
Theodore Li ed39edb906 feat(observability): export Trigger.dev telemetry to Grafana Cloud OTLP (#4583)
* feat(observability): export Trigger.dev telemetry to Grafana Cloud OTLP

Wire OTLP HTTP exporters for traces, logs, and metrics from the
Trigger.dev runtime to Grafana Cloud. Auth uses Basic with instance ID
and API token. Gated behind GRAFANA_OTLP_ENDPOINT, GRAFANA_INSTANCE_ID,
and GRAFANA_API_TOKEN — all three must be set together or all unset;
partial config throws at startup.

* improvement(observability): use OTLP HTTP/JSON for metrics for consistency with traces and logs

* feat(observability): tag Trigger.dev telemetry with deployment.environment.name

* improvement(observability): switch Grafana telemetry vars to OTLP-shaped trio
2026-05-13 19:38:20 -04:00
Waleed ec936be5ca improvement(workflow-block): support manual workflow ID via advanced mode (#4573)
* improvement(workflow-block): support manual workflow ID via advanced mode

* fix(input-mapping): resolve workflowId via canonical hook for advanced mode

* fix(input-mapping): fall back to manualWorkflowId in preview context

* refactor(input-mapping): resolve workflowId via useDependsOnGate canonical pattern
2026-05-12 16:39:28 -07:00
Siddharth GanesanandTheodore Li 0b2cfaf7f7 feat(mothership): add superuser env selection (#4558)
* feat(table): live cell updates via SSE + per-table event buffer

Replaces the polling-based row refetch with a push-based SSE stream that
patches the React Query cache directly as cell-state events arrive.

Architecture:
- New per-table event buffer in apps/sim/lib/table/events.ts. Redis sorted-set
  with monotonic eventId, 1h TTL, 5000-event cap, in-memory fallback. Modeled
  after apps/sim/lib/execution/event-buffer.ts but stripped of complexity
  tables don't need (no per-execution lifecycle, no id-batching, no write
  queue serialization). ~150 lines instead of 700.
- writeWorkflowGroupState appends a fat event after each successful 'wrote'.
  Status transitions carry executionId + jobId; terminal/partial transitions
  also include the new output values inline so the client can patch row data
  without a follow-up refetch.
- New SSE route at /api/table/[tableId]/events/stream?from=<lastEventId>.
  Replays from buffer on connect, polls at 500ms (mirrors workflow execution
  stream), heartbeat every 15s, signals 'pruned' if the caller fell off the
  back of the buffer.
- Client hook useTableEventStream subscribes via EventSource. Reconnect-resume
  with last-seen eventId. On 'pruned', invalidates the rows query and resumes
  from the new earliest. Cache patches walk every cached query under
  rowsRoot(tableId) so filter/sort variants all stay live.
- Removes refetchInterval from useTableRows and the per-page polling effect
  from useInfiniteTableRows. React Query's refetchOnWindowFocus +
  refetchOnReconnect cover the durability gap if any push is dropped.

Out of scope:
- Bulk-cancel events (cancellation path is being redesigned separately).
- Generalizing the workflow event-buffer module to a shared primitive (defer
  until a third use case appears; for now the table buffer is the simpler
  cousin of the workflow one).

* fix(table): drop run-mutation refetch so SSE patches aren't overwritten

useRunColumn.onSettled was canceling in-flight queries and invalidating the
rows query — leftover behavior from the polling era. With the SSE stream
now keeping the cache live via incremental patches, this refetch races the
stream and snaps the cache back to whatever DB shows at the refetch moment,
which can lag the just-arrived queued/running events. Cells appeared stuck
on the optimistic 'pending' even though the SSE was delivering the real
transitions.

* chore(table): simplify SSE plumbing — reuse helpers, drop dead polling code

- Reuse snapshotAndMutateRows for SSE cache patches instead of reimplementing
  the page-walk + cache-shape detection. Adds a {cancelInFlight: false} opt
  for the SSE caller (mutations still cancel as before).
- Drop client-side type duplication in use-table-event-stream — import
  TableEvent and TableEventEntry from lib/table/events directly.
- Drop the now-dead mergePagePreservingIdentity + rowEqual from tables.ts;
  their only caller was the polling effect that was removed earlier.
- Drop the defensive try/catch around appendTableEvent in cell-write — the
  function is documented as never-throwing (returns null on failure).
- Combine INCR + ZADD into one Lua eval in events.ts. Halves Redis RTT per
  cell-write. Lua returns the new eventId; the script splices it into the
  pre-built entry JSON.
- Trim refs to plain let bindings inside the effect; trim stale
  comments referencing the old polling implementation.

* fix(table): address PR review on SSE buffer

- TTL-expiry silent miss: when all keys expire, hgetall(meta) returns empty
  so earliestEventId is undefined and the prune branch was skipped. Reconnect
  with non-zero afterEventId now checks the seq counter — its absence (TTL
  expired) signals pruned so the client refetches. Memory fallback mirrors.
- Unbounded ZRANGEBYSCORE: cap reads at TABLE_EVENT_READ_CHUNK = 500 events
  per call. The route's 500ms poll loop drains chunks across ticks instead of
  flushing 5000 entries (multi-MB) in one tick after a long disconnect.
- Pruned handler closes EventSource client-side: server-side close was firing
  onerror and routing through the 500ms backoff path. Now we close
  proactively, reset the reconnect attempt counter, and reconnect immediately
  from the new earliest.

* Cross env copilot

* Force deploy

* Run migration

* Updates

* Fix migration

* Redeploy

* Make dev db push

* restore old migs

* Cross env copilot

* Add custom tools, skills, mcps to mothership

* Update migration

* Fix migs

* UPdate

* Fix types

* Fix

---------

Co-authored-by: Theodore Li <theo@sim.ai>
2026-05-11 16:58:48 -07:00
Waleed 8774f5c341 fix(deps): patch next-mdx-remote and opentelemetry CVEs (#4557)
- bump next-mdx-remote 5.0.0 → 6.0.0 (GHSA-g4xw-jxrg-5f6m / CVE-2026-0969, arbitrary code execution in MDX serialize)
- bump @opentelemetry/sdk-node and exporter-trace-otlp-http 0.200.0 → 0.217.0 (GHSA-q7rr-3cgh-j5r3 / CVE-2026-44902, Prometheus exporter DoS)
- align @opentelemetry/sdk-trace-base, sdk-trace-node, resources to ^2.7.0 to keep all @opentelemetry/* packages on a single core@2.7.1 instance
2026-05-11 15:06:12 -07:00
WaleedandCursor 3ed8615b5d chore(cleanup): react-doctor dead code elimination, landing + docs overhaul, component modernization (#4544)
* fix(react-doctor): remove unused export types, useEffect clearTimeout missing, a11y fixes

* fix(react-doctor): strip unused export types from contracts, copilot, stores, and components

Remove export keyword from type/interface declarations confirmed to have zero importers
across lib/api/contracts/tools/aws/, lib/api/contracts/*.ts, lib/copilot/generated/,
stores/workflows/workflow/types.ts, ee/access-control, ee/data-retention, lib/logs/types.ts,
and app/workspace component files. TypeScript and API validation both pass clean.

Reduces unused-types count from 394 → 181 and fully eliminates the ✗ critical
dead-code categories (exports, types, files now show as ⚠ warnings not ✗ errors).

* docs improvements

* fix(react-doctor): delete 50 unused files (dead barrels, unreachable components, stale utilities)

Remove confirmed-unused barrel index.ts files across stores/, connectors/, executor/,
lib/, and app/workspace/ that had zero importers. Also delete unreachable components
(chat-history-skeleton, trace-spans, logs-list, template-profile, enterprise landing
sections), stale utilities (buffered-stream, blob-to-data-url, queued-workflow-execution,
compute-edit-sequence), and obsolete generated/contract files. TypeScript passes clean.

* remove dead code

* cleanup

* more

* fix(blog): restore DiffControlsDemo for v0-5 blog post

* fix: restore ContactButton for enterprise blog post, export WIKIPEDIA_PAGE_CONTENT_OUTPUT_PROPERTIES

* fix(react-doc): restore stripped exports and remove server-only dependency

* chore(deps): update lockfile after removing server-only

* added back some exports

* docs

* more

* fix type issues

* tc

* fix docs search route

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(tag-dropdown): add missing isEqual import from es-toolkit

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-10 12:00:05 -07:00
Vikhyath Mondreti 94f60e79b2 improvement(deps): remove unused remark deps (#4542) 2026-05-09 16:35:14 -07:00
Vikhyath Mondreti 02f381d08c feat(pptx): replace viewer with custom preview (#4536)
* feat(pptx): replace viewer with custom preview

* address comments

* address comments

* address bugbot

* address comment

* rm bun lock

* resolve conflict

* update credit comment

* address comments

* rm bun lock

* resolve conflict

* make previews consistent for zoom and scroll

* zoom fix
2026-05-09 14:31:31 -07:00
Waleed c7130c6bf9 fix(tables): fix bulk ops truncation for tables larger than one page (#4532)
* fix(tables): fix bulk ops truncation for tables larger than one page

Bulk operations (column-header delete, select-all copy/cut/delete/run)
were silently truncated to the first 1000 rows because handlers only
iterated the loaded pages from useInfiniteQuery.

Fix:
- Extract tableRowsInfiniteOptions factory (infiniteQueryOptions) so
  the hook and imperative drain share the same typed cache key
- Add background drain via useEffect watching hasNextPage/isFetchingNextPage
  — chains fetchNextPage until getNextPageParam returns undefined
- Add ensureAllRowsLoaded to use-table: reads cache via getQueryData +
  calls fetchNextPage in a while loop until the last page is partial
- Await ensureAllRowsLoaded at every kind:'all' bulk-op entry point in
  table-grid (column delete, copy, cut, action-bar delete/run)
- Add chunkBatchUpdates to send updates in MAX_BULK_OPERATION_SIZE=1000
  chunks so server validation never rejects oversized batches
- Fix undo-redo: make executeAction async and chunk clear-cells,
  update-cells, and delete-column cell-restore with mutateAsync loops

Tests: 41 passing across use-table, tables queries, and use-table-undo

* chore(tables): remove extraneous comments

* fix(tables): add missing useEffect import; chunk range-selection delete and cut

* fix(tables): add hasRunningGroupExecution and import it

* fix(tables): define mergePagePreservingIdentity helper used in polling cache merge

* fix(tables): define ROWS_POLL_INTERVAL_WHILE_RUNNING_MS constant used in polling loop

* fix(tables): capture rowSel before await in delete handler; handle clipboard NotAllowedError

* fix(tables): abort cut on clipboard NotAllowedError to prevent silent cell deletion

* fix(tables): push undo before chunkBatchUpdates to survive partial chunk failures

* fix(tables): use text input for number cells; idle poll backoff; csv error toast; column-cut drain

* fix(tables): audit fixes — column-copy drain, polling scope, merge identity

- Fix polling tick: move Promise.all inside else-branch so dirty[] stays in
  scope; keep hasDirty=true during active mutations so the short interval
  fires while chunked batch-updates are in flight
- Add isColumnSelectionRef branch to handleCopy (mirrors handleCut fix):
  column-header Cmd+C now drains all pages before building clipboard content
- Replace String(updatedAt) comparison in mergePagePreservingIdentity with
  Date.getTime() equality — handles ISO vs +00:00 timezone variants
- Remove redundant batchUpdates.length > 0 guards at chunkBatchUpdates
  callsites (empty-array case is handled inside the function)
- Export _mergePagePreservingIdentity for unit testing
- Add 6 unit tests covering mergePagePreservingIdentity edge cases

* improvement(tables): cleanup — extract components, stabilize callbacks, fix ref sync

* improvement(tables): remove polling, eager drain, and parallelize batch updates

- Drop the per-page polling loop — SSE stream already patches execution
  cell state in real time and invalidates on buffer prune; polling was
  redundant and burned CPU/network on every open table
- Remove eager mount drain (fetchNextPage loop in use-table.ts); scroll
  handler and ensureAllRowsLoaded handle progressive/on-demand loading
- Parallelize chunkBatchUpdates with a 3-worker pool instead of serial
  chunks, reducing bulk-op round-trips by ~3x
- Delete mergePagePreservingIdentity and its tests (no longer called)

* chore(tables): remove stale comments and dead defensive code

- Fix chunkBatchUpdates JSDoc to reflect parallel dispatch (was "sequentially")
- Inline CHUNK_CONCURRENCY=3, single-use constant needs no abstraction
- Drop stale "Polls while any cell is in flight" from useTableRows JSDoc
- Remove two generic "Validation errors surfaced by caller" comments
- Remove ASCII separator line from workflow group mutations section
- Remove dead `if (!variables) return` guard in useImportCsvIntoTable onSettled
  (TanStack v5 always provides variables to onSettled)

* fix(tables): run-all selection sends all rows to server, not just loaded pages

When rowSelection.kind === 'all', selectedRunScope now flags allRows: true.
The action-bar run handlers pass rowIds: undefined to the server when allRows
is set, matching the server contract (missing rowIds = run all eligible rows).
Stop likewise routes through scope: 'all' instead of per-row cancels.

Previously, selecting all rows and clicking Run would silently only run the
rows loaded in the current infinite-query cache (potentially one page of 1000
on a 5000-row table).

* test(tables): remove background-drain describe block (behavior intentionally removed)

* fix(tables): surface CSV import error toast; remove dead hasRunningGroupExecution

* fix(tables): make runWithoutRecording async-aware so undoRedoInProgress covers full undo execution

* feat(tables): render URL cells with favicon and clickable link

* feat(tables): clickable URL cells with favicons using tldts
2026-05-09 13:21:42 -07:00
Waleed 1d3ca79779 chore(deps): audit and clean up dependencies (#4531)
* chore(deps): audit and clean up dependencies

- Remove unused: chalk, chart.js, dotenv, encoding, entities, thread-stream, uuid, @opentelemetry/exporter-jaeger, critters, marked, redis, soap
- Replace soap with hand-rolled Workday SOAP client
- Migrate marked to unified pipeline for inbox responses
- Bump zustand v5, @react-email/*
- Align all @aws-sdk/* to 3.1032.0
- Move type-only deps to devDependencies
- Remove duplicate drizzle-orm/postgres overrides

* fix(workday): coerce SOAP scalar strings to typed booleans/numbers

- XML parser returns leaf text as strings; `!"false"` evaluated to
  `false`, causing all organizations to report `isActive: false`
- Add parseSoapBoolean and parseSoapNumber helpers and apply at consumer
  sites (Inactive, Total_Results)
- Drop unused service/soapAction fields from WD_OPERATIONS map

* fix(workday): coerce compensation amounts and guard Date marshaling

- get-compensation returned Amount/Per_Unit_Amount/Individual_Target_Amount
  as strings (XML leaf text), violating the tool's number contract
- Coerce via parseSoapNumber and widen plan type to number | string
- Add defensive Date branch in marshal() so Date inputs serialize as
  ISO 8601 instead of String(date)
2026-05-08 23:21:09 -07:00
Waleed 11563cfbc2 improvement(uploads): migrate remaining FormData uploads to presigned PUT (#4509)
* fix(uploads): switch mothership uploads to presigned PUT pattern

* fix(uploads): drop unreachable size guard in mothership presigned branch

* improvement(uploads): migrate profile-picture and workspace-logo uploads to presigned PUT

* improvement(uploads): migrate workflow file-upload sub-block to presigned PUT

* improvement(uploads): migrate execution-trigger file uploads to presigned PUT

* fix(uploads): tighten permission checks and fix multipart customKey for mothership/execution

* fix(uploads): require workspace write+ for execution presigned, admin-only for workspace-logos, suppress doubled error toast

* fix(uploads): skip per-file invalidation in batch + extract shared API fallback

- Add skipInvalidation flag to useUploadWorkspaceFile; file-upload sub-block now invalidates once after the batch instead of per file
- Extract uploadViaApiFallback to lib/uploads/client/api-fallback.ts (DRY across 3 hooks)
2026-05-07 20:18:00 -07:00
Vikhyath Mondreti 09425553b3 fix(sanitize): repair malformed subblock states (#4490)
* fix(sanitize): repair malformed subblock states

* address comments

* address comment
2026-05-07 00:54:38 -07:00
WaleedandClaude Opus 4.7 28e60bfd4e fix(docker): drop scripts/ from workspaces array (#4484)
`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>
2026-05-06 20:25:07 -07:00
WaleedandClaude Opus 4.7 7953c56aca fix(security): xlsx CVE bump and bundled security hardening (#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 #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>
2026-05-06 16:36:51 -07:00
Waleed 48331451b0 chore(deps): upgrade next.js to 16.2.4 (#4460)
* 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).
2026-05-06 11:08:01 -07:00
7b6aa728ae improvement(resolver): use context variables for block outputs in function block code (#4223)
* v0.6.29: login improvements, posthog telemetry (#4026)

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

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

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

* fix(signup): fix turnstile key loading

* fix(login): fix captcha header passing

* Catch user already exists, remove login form captcha

* fix: 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 #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>
2026-05-06 00:25:41 -07:00