mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-22 05:19:54 +08:00
1ff445ae80e88e5ae0ffa0fad336eb60a2dd67dc
4843
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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 |
||
|
|
3cedac8e82 |
fix(security): authz, IDOR, and abuse-prevention fixes (#4944)
* fix(knowledge): require write access for batch chunk operations The PATCH /api/knowledge/[id]/documents/[documentId]/chunks handler performs enable/disable/delete operations but authorized callers with only read-level access (checkDocumentAccess). This let read-only workspace members destroy or disable indexed chunks. Switch to checkDocumentWriteAccess (write/admin required), matching the sibling POST/PUT/DELETE chunk mutation endpoints. * fix(env): restrict decrypted workspace env vars to secret admins GET /api/workspaces/:id/environment returned decrypted workspace environment variables to any member, including read-only collaborators, leaking API tokens, database URLs, and other secrets. Mask workspace variable values for non-admin viewers while preserving the variable names, so editor autocomplete and conflict detection keep working. A value is revealed only when the caller is a credential admin of that key, or — for legacy keys with no per-secret ACL — holds workspace admin permission. This mirrors the per-key edit gating already enforced by PUT/DELETE: if you can administer a secret, you can read it. Personal variables and execution-time resolution are unchanged. * fix(files): block cross-tenant deletion via client-controlled context POST /api/files/delete trusted a client-supplied `context`, letting any authenticated user delete another tenant's file by naming an arbitrary key with `context: "og-images"`. verifyFileAccess() short-circuited the three public contexts (profile-pictures, og-images, workspace-logos) to `true` before any ownership/requireWrite check. - Derive the storage context strictly from the trusted key prefix in the delete route; reject a supplied `context` that disagrees with the key. - Gate the public-context short-circuit to reads only. Destructive ops (requireWrite) now prove ownership via verifyPublicAssetWriteAccess: workspace-logos require write/admin on the bound workspace, profile-pictures require an exact owner match, og-images always deny. Reads of public assets are unchanged. * fix(telegram): verify X-Telegram-Bot-Api-Secret-Token on inbound webhooks Telegram triggers accepted any forged update from anyone who knew the webhook URL path: verifyAuth was a no-op that always returned null, and setWebhook registered no secret_token. Generate a per-webhook secret in createSubscription, register it with Telegram as secret_token, and persist it to providerConfig. verifyAuth now fails closed — rejects when no token is configured, when the X-Telegram-Bot-Api-Secret-Token header is absent, or when it does not match via constant-time safeCompare. * fix(security): pin DNS for Agiloft directExecution and Grafana update tools The Agiloft directExecution tools (read/create/search/update/delete/lock/ saved_search/select/get_choice_line_id/remove_attachment/attachment_info) and the Grafana update_dashboard/update_alert_rule postProcess hooks issued outbound HTTP to a fully user-controlled host (instanceUrl/baseUrl) via the global fetch(), guarded only by the synchronous validateExternalUrl() — which never resolves DNS, so a hostname resolving to an internal/reserved IP passed validation (SSRF). Route all of these through the codebase's standard SSRF-safe path: - Agiloft: moved executeAgiloftRequest into utils.server.ts where the existing pinned helpers live. It now resolves+validates the instance URL once and pins every hop (login, operation, logout) to that IP via secureFetchWithPinnedIP. The 11 tool configs now import it from utils.server; URL builders stay in the client-safe utils.ts. - Grafana: the postProcess POST/PUT now uses validateUrlWithDNS + secureFetchWithPinnedIP, matching the already-pinned initial GET. This completes the Agiloft SSRF pinning started in #4639 (which covered the attach/retrieve API routes) by closing the directExecution path, and extends the same guard to the Grafana update tools. * fix(api): enforce workspace allowPersonalApiKeys policy on v1 surface The external v1 API authenticated API keys without evaluating the per-workspace allowPersonalApiKeys setting, so a personal API key could read and mutate a workspace's resources (workflows, tables, files, knowledge, logs) even when the workspace had explicitly disabled personal keys. The same control is already enforced on the workflow-execution surface. Enforce the policy in checkWorkspaceScope (covering validateWorkspaceAccess too): reject personal keys with 403 when the workspace has allowPersonalApiKeys=false. checkWorkspaceScope becomes async; all v1 route callsites updated to await it. * fix(billing): close usage-cap admission race with atomic reservation The server-side usage-limit gate read already-recorded cost, but cost is only written when an execution finishes. A burst of concurrent executions all observed the same pre-burst usage, all passed the cap, and all ran — collectively spending far past the limit before any cost landed in the ledger (free-tier abuse / hard-cap defeat). manual/chat triggers also skip rate limiting, removing the only throttle. Add an atomic check-then-reserve admission step (Redis Lua) that bounds in-flight, un-costed executions per billing entity by both a per-plan concurrency cap and remaining usage headroom, so recordedUsage + reservedSlots * estimate <= limit always holds. The slot is released at execution completion via LoggingSession (skipped on pause; TTL self-heals crashes). Runs for all trigger types, covering the previously-unthrottled manual/chat paths. Fails open when billing is disabled or Redis is unavailable, matching the rate limiter — a Redis blip can't turn into an execution outage, and the recorded-usage gate still runs. * fix(workflows): validate folderId belongs to workflow's workspace on create/update/reorder Reject a folderId that references a folder in a different workspace (or an archived/non-existent folder) before writing it to workflow.folderId. Previously create, update, and reorder only checked workspace permission on the workflow and the folder's lock status, never that the folder lived in the workflow's own workspace, allowing a dangling cross-workspace folder reference. Adds isFolderInWorkspace/assertFolderInWorkspace + FolderNotFoundError to @sim/workflow-authz (mirroring assertTargetFolderMutable in the duplicate path), enforced in performCreateWorkflow, performUpdateWorkflow, and the reorder route. Invalid folders now return 400. * fix(folders): validate parentId against workspace on create/update/reorder Folder write endpoints accepted a caller-supplied parentId and persisted it without verifying the parent existed in the same workspace, and the create and reorder paths had no cycle guard. A workspace member with write access could reparent a folder to a foreign-workspace folder, a non-existent id, or (via reorder) into a cycle, hiding the folder and its workflows from all members. - performCreateFolder: reject self-parenting and validate the parent exists in the workspace and is not archived (mirrors the duplicate route). - performUpdateFolder: add the same workspace/archived parent check alongside the existing circular-reference guard. - folders/reorder: validate every target parent against the workspace, detect cycles in the resulting parent graph (catches batch cycles), and normalize falsy parentId to null to prevent orphaning. Adds tests for cross-workspace parent rejection and batch-cycle rejection. * chore(knowledge): drop non-TSDoc inline comments from chunks route * fix(webhooks): fail closed when HMAC signing secret is not configured Inbound webhook signature verification failed open for HMAC providers (GitHub, Intercom, Jira, JSM, Confluence, Cal.com, Notion, Greenhouse, Typeform, Fireflies, Circleback): when no signing secret was stored, verifyAuth returned null and the workflow executed on a fully attacker-controlled body. Reject these deliveries with 401 instead, matching the fail-closed Stripe/WhatsApp/Vercel providers. Run provider reachability/verification handshakes (Notion verification_token, Grain/Intercom ping) ahead of auth so the pre-secret setup handshake still completes — those return a canned 200 without executing the workflow, and real event payloads fall through to fail-closed verification. Update the trigger secret-field copy to state the secret is required for deliveries to be accepted (was misleadingly marked optional). * style(files): trim verbose inline comments on delete authorization fix * fix(auth): close account-enumeration oracle on email sign-up The custom before-hook pre-check threw a distinguishing 422/USER_ALREADY_EXISTS for already-registered emails, letting an unauthenticated attacker enumerate accounts — defeating better-auth's own OWASP enumeration protection (active under requireEmailVerification). Remove the pre-check and rely on better-auth's generic duplicate-sign-up response, wiring: - onExistingUserSignUp: notify the real account owner out-of-band, mirroring the privacy-preserving forget-password flow. - customSyntheticUser: include admin (role/banned/banReason/banExpires) and Stripe (stripeCustomerId, billing-gated) user fields so the fake response shape is byte-identical to a real new-user response. Adds an ExistingAccountEmail template + 'existing-account' subject. * style(tools): drop non-TSDoc inline comments from Grafana/Agiloft SSRF tools * chore(api): trim extraneous inline comments in v1 logs/files routes Remove a redundant size annotation and two verbose multi-line materialization comments whose intent is already clear from the code. Load-bearing comments (race-condition and key-translation notes) kept. * fix(billing): exclude table-cell dispatch from admission reservation Table-cell dispatch is row-bounded, async rate-limited, and already surfaces a graceful usage state. Applying the in-flight concurrency reservation there turned its 429 into a hard cell error on a normal >15-concurrent-cell run (only 402 was handled gracefully). Skip the reservation for that surface via a new skipConcurrencyReservation option (the usage-cost cap is still enforced), and tidy the reservation comments to TSDoc. * fix(chat): rate-limit and constant-time password auth for public chats Password-protected public chat (POST /api/chat/[identifier]) had no throttling on the password check and compared with a non-constant-time !==, allowing unlimited brute-force and per-character timing leaks. - Add per-IP rate limiting (10 / 15min) to the password branch of validateChatAuth, mirroring the OTP/SSO endpoints; return 429 with Retry-After. Only explicit unlock attempts consume tokens — message sends carry no password and ride the auth cookie. - Replace password !== decrypted with safeCompare. - Fails open on rate-limiter storage errors; no availability regression. * fix(security): cap JSON request body size and gate public chat endpoint The shared parseJsonBody helper (behind parseRequest, used by nearly every contract route) read request bodies with no size limit, buffering the full body into memory before validation. The unauthenticated public deployed-chat endpoint reached this sink with no admission gate, enabling an anonymous memory-exhaustion DoS. - parseRequest/parseJsonBody now enforce a byte cap via a size-limited stream read (content-length precheck + streamed cap), returning 413. Default is API_MAX_JSON_BODY_BYTES (50 MB), overridable per route via maxBodyBytes. Decoding uses TextDecoder to match request.json() BOM handling. - Public chat POST is wrapped with the admission gate (tryAdmit) and passes an explicit CHAT_MAX_REQUEST_BYTES (20 MB) cap. - Chat body contract gains .max() bounds on input, password, conversationId, file data/name/type, and files array length. - Admin bulk workspace import opts into a higher 100 MB cap to avoid regressing large multi-workflow imports. * fix(chat): rate-limit and constant-time password auth for public chats Password-protected public chat (POST /api/chat/[identifier]) had no throttling on the password check and compared with a non-constant-time !==, allowing unlimited brute-force and per-character timing leaks. - Add per-IP rate limiting (10 / 15min) to the password branch of validateChatAuth, mirroring the OTP/SSO endpoints; return 429 with Retry-After. Only explicit unlock attempts consume tokens — message sends carry no password and ride the auth cookie. - Replace password !== decrypted with safeCompare. - Fails open on rate-limiter storage errors; no availability regression. Reinstates the fix reverted by an intervening commit. * fix(billing): never block a lone execution on usage headroom The admission reservation tapered allowed concurrency by remaining usage headroom. With under one credit of headroom left (but not yet over the cap), floor(headroom / estimate) hit zero and rejected even a single, zero-concurrency execution — stricter than the recorded-usage gate, which would have allowed that last run, and with a misleading "too many concurrent executions" message. Floor the headroom term at 1 so a lone execution is governed only by the cost gate; concurrency above the first slot still tapers with headroom. * refactor(env): document workspace env masking, drop inline comments Extract the workspace-env value masking into a TSDoc-documented maskWorkspaceEnvForViewer helper and remove the redundant inline comments from the GET handler and its test. No behavior change. * refactor(env): convert PUT/DELETE authz comments to TSDoc Move the tiered-authorization rationale for the workspace env upsert and delete handlers into TSDoc blocks and drop the inline comments. No behavior change. * fix(telegram): keep legacy webhooks working via Telegram source-IP fallback The secret-token check rejected every webhook registered before secret_token support, breaking live triggers until re-saved. Fall back to verifying the request originates from Telegram's published webhook IP ranges when no secret is configured, so existing triggers keep firing with no re-save or migration while forged updates from arbitrary hosts are still rejected. Webhooks with a registered secret continue to use strict constant-time token verification. * fix(chat): restore constant-time password auth and IP rate limit A billing commit ( |
||
|
|
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> |
||
|
|
540e608e00 |
improvement(chat-voice): modernize ElevenLabs TTS to Flash v2.5 (#4943)
* improvement(chat-voice): modernize ElevenLabs TTS to Flash v2.5 - Switch default TTS model from eleven_turbo_v2_5 to eleven_flash_v2_5 (ElevenLabs recommends Flash over Turbo in all cases; ~75ms latency) - Drop deprecated optimize_streaming_latency knob plus legacy use_pvc_as_ivc / enable_ssml_parsing flags - Move output_format to the query string and raise it from mp3_22050_32 to mp3_44100_128 for higher audio quality - Switch apply_text_normalization from off to auto for correct number/date pronunciation * improvement(chat-voice): default to Jessica voice (Flash v2.5-optimized) Replace the legacy Sarah default (EXAVITQu4vr4xnSDxMaL), which has no high-quality eleven_flash_v2_5 base, with Jessica (cgSgspJ2msm6clMCkdW9) — a current premade conversational voice verified against the live account and optimized for Flash v2.5. |
||
|
|
29ee6a7662 |
fix(secrets): keep readonly secret names legible instead of dimming them (#4942)
* fix(secrets): keep readonly secret names legible instead of dimming them Readonly viewers saw the workspace secret name at opacity-50 while the masked value rendered at full opacity, an inconsistent and hostile treatment of content they need to read. Drop the opacity dim on the non-renameable key; non-editability is already conveyed by the read-only field and absent edit affordances. * fix(secrets): use cursor-text on readonly key to hint selectability |
||
|
|
272bad9718 |
feat(realtime): preflight schema-compatibility check on startup (#4940)
* feat(realtime): preflight schema-compatibility check on startup The socket service authorizes every connection with a full-row query against the workflow table. When a deploy ships a realtime image whose compiled schema is ahead of/behind the live DB (e.g. a column dropped by a migration the image predates), that query fails on every request and silently breaks persistence — yet the process stays up and the shallow /health probe keeps returning 200, so the deploy looks healthy while serving nothing. Run one representative workflow query before listen(): a schema mismatch throws, propagates to the entrypoint, and the task exits non-zero and never goes healthy, so CodeDeploy auto-rolls-back instead of shifting traffic onto broken tasks. Schema-class errors (undefined column/table/function) fail fast; connection-class errors retry with backoff so a cold DB at boot does not flap. Runs once at startup, never on the per-probe LB health check, to avoid a DB blip mass- terminating the fleet (cascading failure). * fix(realtime): unwrap cause for schema codes, drop sleep after final attempt - isSchemaMismatch now walks the error.cause chain — drizzle wraps the driver error, so the SQLSTATE often lives on the inner cause, not the outer throw. Without this a wrapped 42703/42P01 was retried 5x and mis-reported as "database unreachable" instead of failing fast. - No longer sleeps after the final failed attempt (~6-10s of dead wait that undermined the fail-fast contract); sleep now only happens between attempts. - Tests: assert sleep is called exactly 4 times on exhaustion, and add a wrapped-cause fail-fast case. |
||
|
|
b2a485e164 |
fix(db): serialize concurrent migrations with a Postgres advisory lock (#4939)
* fix(db): serialize concurrent migrations with a Postgres advisory lock Deployments start N app replicas at once, each with a migration sidecar. drizzle migrate() has no cross-process lock, so all N read __drizzle_migrations, all see the same migration pending, and all apply it concurrently — one wins, the losers run the same DDL against already-mutated state and exit 1 (e.g. DROP TABLE "form" -> table does not exist / TaskFailedToStart). Wrap migrate() in a session-level pg_advisory_lock so runners serialize: the winner migrates, the losers block, then re-read and find nothing pending. Session locks auto-release on disconnect, so a crashed runner never wedges the lock. * fix(db): guard pg_advisory_unlock so it cannot mask a successful migration If the explicit unlock throws (e.g. connection drops in the window after migrate() commits), the exception bubbled to the outer catch and exited 1 — falsely reporting a failed migration to the deploy orchestrator. The session lock auto-releases on disconnect anyway, so swallow and log instead. * refactor(db): move unlock-guard rationale to TSDoc helper |
||
|
|
62c48bfd31 |
improvement(tools): validate integrations, add Gong activity tools, regenerate docs (#4937)
* improvement(tools): validate integrations, add Gong activity tools, regenerate docs * fix(servicenow): give list-attachments limit a unique subBlock id Read Records and List Attachments shared the subBlock id 'limit', so the single-value-per-id store could bleed the value across operations. Rename the new list-attachments field to attachmentLimit and map it back to the tool's limit param. * fix(servicenow): type upload-attachment response json to fix build typecheck secureFetchWithValidation's response.json() resolves to unknown under the build's stricter typecheck; cast the parsed body so data.result is accessible. * refactor(servicenow): type upload-attachment response per file-route convention Match the SharePoint/OneDrive upload pattern: import the named ServiceNowAttachment type from the tool's types, cast response.json() to it, and extract specific fields with ?? null instead of passing data.result through as an opaque unknown blob. * fix(servicenow): remove invalid 'custom' generationType from groupBy wandConfig 'custom' is not a member of the GenerationType union, breaking the build typecheck. No valid type fits a comma-separated field list, so drop the wandConfig (consistent with the block's other field-list inputs). * fix(servicenow): drop redundant hidden fileContent param from upload attachment Per project rule, visibility:'hidden' is reserved for framework-injected tokens, not user-supplied data. fileContent was a copied-over legacy fallback with no caller (the block uploads via the canonical file/UserFile path), so remove it from the tool, types, contract, and route. * fix(dub): require a link selector for bulk update links Bulk Update Links could run with only Update Data and no Link IDs or External IDs, sending Dub a request with no target links and producing a confusing API error. Guard the tool to throw a clear validation error when neither selector is provided, and clarify the block placeholder. |
||
|
|
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. |
||
|
|
3bf710489d | fix(mothership): clear chat input after sending a message mid-conversation (#4936) | ||
|
|
209501e2da |
feat(slack): assistant thread ops, paginated history/replies, and permalink (#4934)
* feat(slack): add assistant thread ops, paginated history/replies, and permalink - New tools: slack_set_status, slack_set_title, slack_set_suggested_prompts (assistant.threads.*), slack_get_channel_history + slack_get_thread_replies (paginated conversations.history/replies via directExecution), slack_get_permalink - Wire all six as Slack block operations with subBlocks, conditions, params, outputs - Add scopes: assistant:write, im:history, mpim:read, mpim:history (+ consent labels) - Add manifest capabilities: action_read_history, action_assistant - Fix invite_to_conversation no_user error code; widen DM history scope hints - Regenerate tool docs and integrations catalog * fix(slack): correct thread parent on cursor-resume, complete DM scope hints, reject empty suggested prompts - get_thread_replies: identify thread parent by ts === thread_ts instead of assuming index 0, so cursor-resumed pages (which omit the parent) no longer mislabel a reply as the parent - get_channel_history + get_thread_replies: include im:history/mpim:history in the missing_scope hint to match the OAuth grant and DM/MPIM reads - set_suggested_prompts: throw a clear error when no valid prompt is provided instead of silently calling Slack with an empty prompts array * fix(slack): guard non-numeric limit/maxPages; drop ungranted im/mpim history scopes - Add resolvePositiveInt helper and use it for limit/maxPages in get_channel_history and get_thread_replies, so a non-numeric value (e.g. from an LLM) no longer becomes NaN and silently disables pagination (returning an empty success) - Remove im:history/mpim:read/mpim:history from the Slack OAuth grant — the Slack app does not have these scopes; assistant:write is the only added scope - Drop the explanatory inline comment in get_thread_replies * improvement(slack): move history pagination cursor/maxPages to advanced mode Matches the existing paginationCursor convention in the block — keeps the basic UI focused on channel + time filters while pagination internals live under advanced. * fix(slack): narrow set_status missing_scope hint to assistant:write A user hitting missing_scope on set_status already has chat:write (long granted); the scope they actually need is assistant:write, which is also what set_title/set_suggested_prompts point to. Narrow the hint to assistant:write for accurate, consistent guidance. |
||
|
|
efa4f27006 |
fix(agent): unique tool ids for multi-instance tools + icon updates (#4933)
* fix(agent): resolve canonical resource id when building unique tool ids Multi-instance agent tools (table, knowledge base, workflow) get a unique id by suffixing the resource id (e.g. table_query_rows_<tableId>). The suffix code read the canonical param key (tableId/knowledgeBaseId) directly from stored params, but selector subblocks persist their value under the subblock id (tableSelector/knowledgeBaseSelector). Tools stored in that state never got a suffix, so two of them collapsed to the same name. Resolve the canonical resource id from the source subblock first — mirroring the execution-time paramsTransform — so the unique id is derived the same way the tool actually runs. Non-destructive; no-op when the canonical key is already present. * test(agent): cover knowledge-base selector resolution for unique tool ids * improvement(icons): new 1Password icon on white bg, enlarge Prospeo and Neo4j glyphs |
||
|
|
c7689c025f |
fix(tables): key filter UI by stable column id; show column name in delete confirm (#4930)
* fix(tables): key filter UI by stable column id; show column name in delete confirm * fix(tables): import getColumnId from column-keys module, not the server-tainted barrel |
||
|
+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 |
||
|
|
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> |
||
|
|
1e35656af9 | fix(modal): preserve spacing in workspace delete confirmation label (#4929) | ||
|
|
2f70188091 |
fix(tables): route large CSV imports to the background job instead of 413 (#4927)
* fix(tables): route large CSV imports to the background job instead of 413 * fix(tables): drop duplicate error toast on async import failure * fix(tables): guard importId on async cancel and drop mutation objects from deps |
||
|
|
37f1141fc0 |
fix(terminal): truncate console values by size and cycles, not nesting depth (#4924)
normalizeConsoleValue replaced any value at depth >= 6 with [Truncated object], discarding tiny payloads solely for their position in the tree (agent tool-call rows sit at exactly depth 6). The depth cap was also the only guard against infinite recursion on circular structures. Add a path-tracked WeakSet so true ancestor cycles resolve to [Circular] while values shared across sibling positions still render fully, and raise MAX_DEPTH to 12 as a pathological-nesting backstop. Actual size stays bounded by the existing 50KB string cap and 256KB byte cap. |
||
|
|
24f04162fb |
feat(integrations): expand tool coverage, audit integrations, regen docs (#4920)
* feat(integrations): expand tool coverage, audit integrations, regen docs Integrations - Audit and expand Railway, Reddit, Vercel, Sentry, Granola, LaunchDarkly, Infisical, Intercom, Luma, 1Password, NeverBounce, and ZeroBounce tools against their live API docs; correct request/response shapes and outputs - Add new tools across Railway (services, deployment lifecycle, variables), Reddit (mod actions, user/subreddit reads, messaging), Vercel (domains, webhooks, edge config, deployment promotion), Sentry (teams), and Granola (folders); register all in the tool registry - Infisical: fix delete to send params in the JSON body and complete the secret output fields (actor, metadata isEncrypted, rotation/reminder fields) UI - Update the user-input tooltip copy - Adjust the feedback modal textarea sizing Docs - Regenerate tool/trigger docs to match current source * rm comments from registry * feat(integrations): expand Kalshi, Luma, Rootly, Polymarket, Railway tools; regen docs - Add and refine tools across Kalshi, Luma, Rootly, Polymarket, Railway, LaunchDarkly, and Sentry; register all new tools - Regenerate tool docs to match the updated source * chore(integrations): update Kalshi get_balance, drop polymarket test, regen docs * chore(integrations): update Kalshi get_events and subblock migrations, regen docs |
||
|
|
b12b0f1da7 |
feat(models): add Claude Fable 5 (#4921)
* feat(models): add Claude Fable 5 * docs(anthropic): note Fable 5 in buildThinkingConfig docblock |
||
|
|
f7811f8acc |
feat(tables): stable column ids for metadata-only rename (#4898)
* feat(tables): stable column ids for metadata-only rename * fix(tables): address review — id-key exec clears + waiting labels, name in upsert error, un-gate group output ids * fix(tables): id-correct column undo (rename/create/delete) with id reuse on re-add * refactor(tables): mint column ids via generateId (uuid), drop collision-check plumbing * fix(tables): match column id or name when removing column from optimistic delete cache * fix(tables): resolve column storage id once for delete optimistic strips; biome-format 0228 snapshot * fix(tables): make in-grid find id-native (scan/return JSONB keys as column ids) * fix(tables): translate id→name in CSV/JSON export read path (+regression test) |
||
|
|
efeacb9e22 |
fix(tables): stop insert-row flicker and return order_key from rows list (#4918)
* fix(tables): stop insert-row flicker and return order_key from rows list The rows insert flicker came from useCreateTableRow.onSettled invalidating tableKeys.detail(tableId), which prefix-matches the nested rowsRoot rows query and forces an un-cancelled refetch on every insert. A late offset refetch could resolve after the optimistic splice and clobber freshly-inserted rows. - invalidate detail with exact:true (+ lists) so the count surfaces refresh without cascading into the rows query - compare order keys bytewise in reconcileCreatedRow to match the server's COLLATE "C" ordering and fitsAfter (was localeCompare) - include order_key in the GET /rows list response; it was dropped in the route mapping, so the client never saw keys and reconcileCreatedRow always fell back to the position path * fix(tables): scope optimistic insert splice to the default-order view reconcileCreatedRow patched every rows query under rowsRoot, but its orderKey/position heuristic only matches the unfiltered, unsorted server order. Under a filter or column sort the splice could show wrong rows, wrong order, or an inflated totalCount, and without the prior rowsRoot refetch it persisted until the query went stale. - patch only the default-order rows queries (filter and sort both absent) - refetch the filtered/sorted variants on insert; active ones update now, inactive ones on next view. The default view stays optimistic (no flicker) - the find/write subtrees are excluded from the splice (different shape) |
||
|
|
05408fd1ce |
feat(emcn/toast): toast redesign — intent variants, stacking, hover reveal, dismiss-all (#4909)
* feat(emcn/toast): redesign toast — intent variants, Sonner-style stack, hover text reveal, dismiss-all Component (apps/sim/components/emcn/components/toast/toast.tsx): - Variants default/info/success/warning/error, each with a distinct outline icon (CircleAlert/TriangleAlert/CircleCheck/Info/Bell) rendered inline with the message in a neutral color — no badge; intent reads from icon + copy. - Stacking modeled on Sonner/Base-UI: a collapsed pile that fans open upward only when the cards are hovered. One fixed-duration expo-out tween drives all cards so rapid arrivals move in unison (no lagging card); cards arrive collapsed (expand is scoped to a wrapper around the cards, not the dismiss control). - Title vs subtext hierarchy: message is a medium, primary-color title; the optional description is lighter/smaller subtext. - Truncated text reveals its hidden lines on hover (RevealText): only the previously hidden lines blur in; the card height tracks the content so the action button stays pinned (no clipping). Larger bottom gradient fade hints at more text. - Concentric corner radius (16px = chip 8px + 8px padding); single-line cards use a tighter 12px so they don't read as pills. - Dismiss-all control: a small circular chip just outside the stack's bottom-left, shown at 2+ toasts. Linear auto-dismiss ring that restarts on each new arrival, pauses on hover, click to clear all; spring 'pop' entrance. - Bug fixes: route-scoped clearing (toasts no longer trail across navigation), dedup of the add/update double-fire, actionable toasts persist by default. Source/usage: - stores/terminal/console/store.ts: notifyBlockError now passes the block name as the title and the error as the description (title/subtext), plus the dedup window. - app/playground/page.tsx: Toast section added to the EMCN gallery. - New EMCN icons: circle-alert, circle-check, info, triangle-alert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(emcn): rename Info icon to CircleInfo to resolve barrel collision The toast redesign added an `Info` icon, but the emcn barrel already re-exports an `Info` component (`export * from './components'` + `export * from './icons'`), so the top-level `@/components/emcn` had a duplicate `Info` export (TS2308). Rename the icon to CircleInfo — matching its circle-shaped siblings (CircleAlert, CircleCheck) and resolving the collision. The icon is consumed only by toast.tsx. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(emcn/toast): keep persistent toasts alive in a stack; prune stale heights H1 (functional): a stack of 2+ toasts arms the StackDismiss ring, whose 6s countdown called dismissAllToasts() — wiping persistent (duration<=0) actionable toasts like 'Fix in Copilot' before the user could react. Add an `autoDismiss` flag (false when any toast is persistent) that suppresses the auto-countdown while keeping the manual dismiss-all button. H2 (memory): stack-limit eviction (slice) dropped the oldest toast without clearing its `heights` entry, leaking entries over a session. Reconcile heights to live toast ids, mirroring the timer effect's stale-entry cleanup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(emcn/toast): tint variant icons; polish clear-all, countdown reset, teardown - Req 6: per-variant icon tint (error/warning/success/info) from the shared intent palette, so error vs info is distinguishable pre-attentively in a mixed stack instead of only by reading the copy. Default stays neutral. - H3: wrap the stack in AnimatePresence so clear-all / route-change fades the frozen stack out instead of cutting abruptly; per-card exits still play for single dismissals. No pointer-events change (no hover regression). - Countdown reset now keys on a monotonic arrival counter, so dismissing the front card no longer restarts the whole stack's auto-dismiss timer. - Provider teardown only nulls the global toast bindings if they're still its own, guarding against an out-of-order unmount with a second provider. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep modal errors inside the modal instead of a screen-corner toast Per the toast spec (modal errors stay in the modal; only page errors go to the corner), route two in-modal failures inline: - add-people-modal: failures now render via ChipModalError in the modal body (the modal stays open with the failed emails), not toast.error. - slack setup wizard: a clipboard-copy failure shows inline beneath the copy button (mirroring the existing 'copied' state), not a toast. Left as-is (verified correct, not violations): credential-detail save error (a routed page, not a modal — page toast is correct) and import-csv success (fires as the dialog closes — inline isn't possible). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(data-drains): surface CreateDrainModal submit error inline Completes the modal-error migration: the create-drain form's submit failure now renders via ChipModalError in the modal body (the modal stays open) instead of a toast.error that escaped to the screen corner. The success-on-close toast and the page-level drain-row action toasts are unchanged (correct). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(emcn/toast): trim comments to concise TSDoc per project convention Remove inline // and JSX {/* */} comments (the project documents with TSDoc only) and condense multi-paragraph TSDoc to 1-3 lines across toast.tsx, plus the stray inline comments in the console store and slack wizard. Behavior and code are unchanged — verified the non-comment source is byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(emcn/toast): inset on the /w index route; stronger, unified intent tints - Positioning: the workflow-list index route /workspace/[id]/w renders the panel + terminal but failed includes('/w/'), so a toast there rendered under the panel. Match /\/w(\/|$)/ so both the editor and the index inset. - Tint: unify all four intent icons onto the badge palette and use --badge-success-text (a darker green) so success isn't ~1.9:1 / washed out on the light card; error/success now match warning/info's token family. - Gate the card enter animation on reduceMotion for consistency with the rest of the file (behaviour was already correct via duration 0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(upgrade): surface billing errors via toast instead of native alert() The upgrade flow used native alert() for upgrade / switch-plan / switch-interval failures. Route them through the unified toast (toast.error) like the rest of the app — it's inside the ToastProvider tree. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(emcn): drop CountdownRing barrel export + dead toast keyframes Completes removing what the framer-motion toast redesign orphaned: the CountdownRing re-export and the zero-reference notification-/toast-* keyframes. * fix(console): normalize error in notifyBlockError so dedup keys match addConsole passes a normalizeConsoleError'd error while updateConsole passed the raw update.error, so the dedup key (String(error)) differed between the two paths and the duplicate block-error toast could still slip through. Normalize inside notifyBlockError so both paths produce the same key and description. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(emcn/toast): expire timed toasts stacked beside a persistent one Suppressing the stack auto-countdown when a persistent toast is present (so it can't clear the action) also stopped timed toasts in that stack from expiring. Fall back to per-toast timers whenever the stack countdown isn't auto-firing, so timed toasts auto-dismiss individually while the persistent one stays. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(add-people): keep the specific add-failure reason in the inline error The inline ChipModalError dropped the server/validation detail the toast used to show via description. Fold getErrorMessage(firstError) back into the message so users still see why the add failed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(emcn/toast): don't evict a persistent toast at the stack cap slice(-STACK_LIMIT) dropped the oldest card on a 4th arrival even if it was a persistent (actionable) error meant to stay until dismissed. Evict the oldest auto-dismissable toast instead, falling back to the oldest only when every toast is persistent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(console): scope error-dedup to the block execution The dedup keyed on block+message for 1.5s, but the toast stack clears on navigation — so a genuinely new same-block error within that window (e.g. a re-run, or after navigating) was suppressed with no replacement toast. Key the dedup on the block execution (getBlockExecutionKey) so only the same execution's addConsole/updateConsole double-fire collapses; a different execution always toasts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(emcn/toast): let global status toasts survive navigation (persistAcrossRoutes) The route-clear dismissed every toast on navigation, including the persistent realtime connection/reconnect status toast — which then never re-showed (the provider's id ref short-circuited). Add a persistAcrossRoutes flag: the navigation clear now keeps flagged toasts and only drops route-scoped ones, and the WorkspacePermissionsProvider status toasts set it. Page-scoped toasts (including actionable block errors) still clear on navigation as before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(emcn/toast): reset expanded when the stack empties The hover wrapper unmounts without firing mouse-leave when the last toast goes (dismiss / clear-all / navigation), so expanded could stay true and stop the next toasts from auto-dismissing. Force expanded false whenever the stack is empty. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: waleed <walif6@gmail.com> |
||
|
|
a72e35e2f4 |
feat(sendblue): add Sendblue iMessage/SMS integration with tools and triggers (#4917)
* feat(sendblue): add Sendblue iMessage/SMS integration with tools and triggers * fix(sendblue): address review — status-aware webhook dedup, shared routing map, uniform output casing, api-key authType * fix(docs-gen): skip nested array `items` descriptor in tool input tables * chore(sendblue): use SendblueSendStyle type, trim URL identifiers * fix(sendblue): keep is_outbound routing map local to the webhook handler Avoids a webhook-providers -> triggers cross-subgraph import (single source of truth in the handler, its only runtime consumer). * fix(sendblue): remove invalid `tags` from block config (belongs on BlockMeta) |
||
|
|
4f00baf6b4 |
refactor(emcn): make ChipModal footer/header props-driven and migrate all consumers (#4905)
* refactor(emcn): make ChipModal footer/header props-driven and migrate all consumers
* fix(emcn): restore Cancel disable guard for in-flight ChipModal actions
Add `cancelDisabled` to ChipModalFooter and thread the pre-migration
in-flight guards back into all 45 footers that had them, so destructive
flows can no longer be dismissed mid-mutation.
* feat(emcn): add ChipConfirmModal confirmation primitive
Confirmations have a different button grammar than form modals: a named
dismiss decision ('Keep editing') plus a usually-destructive confirm, with
a single dismiss path — not the structural, never-relabeled Cancel that
ChipModalFooter guarantees. Forcing confirmations through the form footer
produced both the ambiguous-'Cancel' copy loss and the header-X/footer-Cancel
state-reset drift.
ChipConfirmModal models that grammar directly and owns the safety rails every
hand-rolled confirm had to remember: header-X / dismiss button / Esc all route
through onOpenChange (teardown can't desync), and 'pending' disables the
dismiss while the action is in flight. Extract a shared ChipModalFooterShell so
the footer chrome stays a single source of truth across both components.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(emcn): migrate all confirmation dialogs to ChipConfirmModal
Migrate ~30 destructive-confirm and unsaved-changes dialogs across the app
from hand-composed ChipModal + ChipModalFooter to the declarative
ChipConfirmModal. Net effect:
- Unsaved-changes dialogs read 'Keep editing' again (was an ambiguous
'Cancel' after the footer migration) via dismissLabel.
- Header-X and dismiss now share one teardown path, fixing the api-keys /
copilot / scheduled-tasks / base-tags / tables cases where dismissing via
the X left the targeted row selected.
- In-flight 'pending' disables the dismiss button uniformly, so destructive
confirmations can't be dismissed mid-mutation.
- Confirm-modal widths harmonized to 'sm' (several were an oversized 'md').
Form/editor modals and the three-way access-control unsaved dialog keep
ChipModalFooter (structural Cancel is correct there). Also restores the
JSON-cell font size in row-modal and the in-flight Cancel guard in
invite-modal flagged in review.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(emcn): normalize confirm pendingLabel ellipsis to '...'
Two dialogs preserved a typographic '…' from their pre-migration copy; the
codebase uses three-dot '...' everywhere else. Align for consistency.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(emcn): restore Enter-to-submit in form modals after footer migration
The props-driven footer renders its primary action as a Chip (type='button'),
so multi-field <form> modals lost implicit Enter submission when the old
type='submit' control was removed. Add a hidden, disabled-mirroring submit
button — the existing codebase idiom (a2a.tsx, mcp.tsx) — to create-base,
row, and help modals so Enter submits exactly as before and still respects
each form's in-flight/validation guard.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: waleed <walif6@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
7434df9457 |
improvement(metrics): emit hosted-key metrics to CloudWatch instead of OTel (#4914)
* improvement(metrics): emit hosted-key metrics to CloudWatch instead of OTel * fix(metrics): await metric flush on shutdown and hard-cap the buffer |
||
|
|
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. |
||
|
|
ffd87a3e8f |
fix(home,integrations): optical-center home input + integrations page render fix (#4916)
* fix(integrations): correct integrationType key so the integrations page renders The merged email-verification blocks (zerobounce, neverbounce, millionverifier) were serialized into integrations.json with a typo'd `integrationTypes: ["sales"]` plural array instead of the singular `integrationType` the catalog reads. They fell into an `undefined` category bucket, and sorting sections by label threw "Cannot read properties of undefined (reading 'localeCompare')", erroring the whole page. - Correct the three entries to `integrationType: "sales"` (matches each block config's IntegrationType.Sales and the generator's output) - Defensively skip any integration without an integrationType when grouping so a single malformed catalog entry can never crash the page again * move home chat to optical center |
||
|
|
e257d067f2 |
feat(integrations): suggest curated skills per integration with one-click add (#4912)
* feat(integrations): suggest curated skills per integration with one-click add Curate research-backed, capability-grounded skills for every catalog integration and surface them on the integration detail page. Each skill maps to operations the block actually supports and can be added to the workspace in one click; track adds in PostHog. - Add SuggestedSkill type + skills field on BlockMeta; populate skills for all 193 catalog integrations (3 audit passes for grounding/sourcing) - getSuggestedSkillsForBlock() with versioned-type (e.g. notion_v2) base fallback - Skills section on the integration detail page with add/added states - integration_skill_added PostHog event with workspace/integration metadata * fix(integrations): flip suggested-skill row to Added immediately after add The row derived Added state solely from the useSkills cache, so between a successful create and the list refetch the row still showed Add and could be clicked again, hitting the server duplicate-name check. Track added names in local state so the row reflects the add immediately. * fix(integrations): harden suggested-skill add flow; document skill authoring Address PR review feedback on the suggested-skills section: - Make useSkills the single source of truth for Added state by writing the created skill into the React Query cache onSuccess (fixes stale Added that survived a delete, and the lag that allowed a duplicate click) - Track in-flight adds in a Set so concurrent adds keep independent pending state and cannot be double-submitted - Surface failures with toast.error instead of swallowing the rejection - Extract the duplicated SkillTile into a shared workspace component Also document the new BlockMeta.skills field in the add-block and validate-integration skills (+ blocks AGENTS.md): skills must be grounded in the block's tools.access and sourced from real online use cases, never invented. * fix(integrations): synchronous in-flight guard for skill add; align cursor docs - Guard handleAdd with a ref so two rapid clicks cannot both fire a create before the disabled state re-renders (pendingNames is async) - Fold the create-cache-merge rationale into the hook's TSDoc and drop non-TSDoc inline comments to match the repo convention - Align .cursor add-block/validate-integration command docs with the newer .claude/.agents versions: BlockMeta section + skills authoring/validation guidance (grounded in tools.access, sourced from real online use cases) * fix(integrations): gate skill Add/Added on authoritative workspace skills useSkills uses keepPreviousData, so during initial load or a workspace switch the list could be empty or a prior workspace's placeholder — making rows show a misleading Add (duplicate-submittable) or a false Added. Derive skillsReady from !isPending && !isPlaceholderData, only mark Added when ready, and disable Add until the current workspace's list has loaded. * chore(integrations): drop local-variable comments in skills section Keep to the repo's TSDoc-on-declarations convention — the in-flight guard and skillsReady derivation are self-evident from naming. |
||
|
|
27fc6ddcc3 |
fix(user-input): atomic chip selection, modifier-key handling, and stale overlay ghost (#4902)
* fix(user-input): atomic chip selection, modifier-key handling, and stale overlay ghost * fix(user-input): track raw selection in ref and sync mention menus on ranged selections * fix(user-input): translate overlay content instead of scrolling the overlay box * fix(user-input): read selection direction at apply time, document edge heuristic * fix(user-input): reconcile textarea DOM with state on selection events * fix(user-input): keep Cmd+Backspace native instead of single-chip delete * fix(user-input): record observed selection before reconcile early-return * refactor(user-input): extract adoptDomValue; drop flushSync pre-paint reconcile * refactor(user-input): native co-scroll for textarea + overlay, remove JS scroll-sync * refactor(user-input): extract pure snapSelectionToChips with unit tests * fix(user-input): track prev selection via selectionchange; make chip deletion undoable * docs(user-input): trim verbose comments in selection logic * fix(user-input): fall back to setRangeText when execCommand delete is unsupported (Firefox) |
||
|
|
25771358d0 |
feat(enrichment): add ZeroBounce, NeverBounce, and MillionVerifier email verification (#4854)
* feat(enrichment): add ZeroBounce, NeverBounce, and MillionVerifier email verification * docs gen * mdx * fix(zerobounce): handle 200-status API errors and guard JSON parsing * fix(byok): render new email-verify providers + raise hosted rate limits - Add zerobounce/neverbounce/millionverifier to the Enrichment PROVIDER_SECTIONS so they render in the workspace BYOK settings UI (they were in PROVIDERS + the API allowlist but no section listed them). - Raise ZeroBounce and MillionVerifier hosted per-workspace rate limits from 60 to 1200 req/min, sized against documented upstream ceilings (ZeroBounce 80k/10s; MillionVerifier 160/sec). NeverBounce stays at 60 pending its account-configured throttle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Theodore Li <theo@sim.ai> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ec256d244f |
fix(tables): compare order_key bytewise (COLLATE "C") to stop insert collation errors (#4908)
* fix(tables): compare order_key bytewise (COLLATE "C") to stop insert collation errors * fix(tables): detect mis-keyed tables by position order, not order_key sort * improvement(tables): make repair-script dry-run lock-free and clearly labeled |
||
|
|
d526b23841 |
refactor(mothership-chats): rename task feature to chat, move route, add redirect (#4910)
* refactor(mothership-chats): rename task feature to chat, move route, add redirect - Move workspace route /task/[taskId] -> /chat/[chatId] and add a permanent redirect in next.config so existing bookmarks/deeplinks keep working - Rename client hooks queries/tasks.ts -> queries/mothership-chats.ts with the mothership-chat-prefixed family (distinct from the existing deployed-chat hooks in queries/chats.ts to avoid query-key collisions) - Rename contract mothership-tasks.ts -> mothership-chats.ts (MothershipChat) - Rename lib/copilot/tasks.ts -> chat-status.ts (chatPubSub, ChatStatusEvent) - Rename use-task-events -> use-mothership-chat-events, use-task-selection -> use-chat-selection, and folder-store chat-selection methods - Update server-generated deeplinks (oauth callback, inbox response, inbox list) - Preserve wire/persisted/analytics values that cross process or deploy boundaries: Redis channel task:status_changed, SSE event task_status, MothershipResourceType 'task', posthog event names, drag/itemType discriminants. Unrelated scheduled-tasks and email-inbox tasks untouched * refactor(mothership-chats): use skipToken in useMothershipChatHistory Drop the enabled + chatId! non-null assertion in favor of the skipToken pattern, matching useMothershipChats. Behavior-preserving. |
||
|
|
2c7b1ca54d |
improvement(perms): member removal reassignment policies (#4906)
* improvement(perms): member removal reassignment policies * improve UI notif * address comments |
||
|
|
c90a1eb4a6 |
feat(tables): row-gutter drag-select, Cmd+F find, and select-all polish (#4901)
* feat(ui): allow dragging on row gutters * feat(tables): Cmd+F find across cells + row-gutter UI polish Find: - New GET /api/table/[tableId]/rows/find endpoint + contract + useFindTableRows hook - findRowMatches: case-insensitive substring search across every cell via a row_number() CTE + jsonb_each_text + ILIKE, returning each matching cell with its ordinal in the filtered/sorted view. Shared buildRowOrderBySql keeps the find ordinals aligned with the paginated list (with an id tiebreak). - Cmd/Ctrl+F floating find box (search-on-Enter), cell-by-cell next/prev that loads the target row then reveals the cell via the existing anchor scroll. Gutter UI: - Row number/checkbox centered in the region left of the run button; the select-all header checkbox centers in the same region so they line up. - emcn Checkbox gains an indeterminate (minus) state; select-all shows the minus on any partial selection and clears everything on click. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(tables): inline rename from the list context menu Wire the existing "Rename" item in the per-table context menu to inline rename (useInlineRename + InlineRenameInput via the Resource name cell's `content` override), matching the Files list. Enter/blur saves, Esc cancels; replaces the prior modal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style(tables): restyle Cmd+F find box to match workflow search Reuse the workflow search visual language (surface-1 panel, emcn Input, muted "k of N" counter, size-8 ghost chevron buttons) instead of the flat custom input. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(resource): move "Clear sort" to top of the sort menu Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
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 |
||
|
|
20a00a181d |
fix(security): SSRF pinning, Twilio webhook auth, copilot token leak, audit-log tenant scoping (#4899)
* fix(clickhouse): pin outbound HTTP connection to validated IP (DNS rebinding) clickhouseRequest() validated config.host via validateDatabaseHost() but discarded the resolved IP and called fetch() with the original hostname, triggering a second DNS lookup. A workflow author controlling the host parameter could use DNS rebinding to pass validation against a public IP and then connect to an internal/private address (SSRF). Replace fetch() with secureFetchWithPinnedIP(), connecting to the validated resolvedIP while preserving the hostname for Host/TLS SNI — the same DNS-pinning pattern used by the other DB tools. Set Content-Length explicitly so request framing is identical to the previous fetch. Add tests locking the contract: connection targets the validated IP not the hostname, no request is issued on validation failure, http/https and allowHttp are selected from secure, and body/headers propagate. * fix(mcp): pin auth-type probe to validated IP to close SSRF/DNS-rebinding window The MCP auth-type probe (detectMcpAuthType) issued raw, unpinned fetch() calls against the user-supplied server URL, re-resolving DNS independently of validateMcpServerSsrf. This re-opened the exact DNS-rebinding (TOCTOU) window the pinned McpClient path was built to close: a hostname that resolves to a public IP during validation could resolve to an internal IP during the probe. The probe now pins to the IP already validated by the caller via createMcpPinnedFetch(resolvedIP); when no pre-validated IP is available it falls back to createSsrfGuardedMcpFetch(), which validates and pins each request. The best-effort session-close DELETE reuses the same pinned fetch. Both call sites (test-connection route and performCreateMcpServer) thread the resolved IP into the probe. * fix(security): stop returning plaintext OAuth access tokens from copilot credentials GET /api/copilot/credentials returned each connected account's live, post-refresh OAuth access token in plaintext to any session for that user. The endpoint is only used for credential display/masking and no client reads the token, so drop accessToken from the get_credentials tool output and the copilot credentials response contract. Also removes the incidental refreshTokenIfNeeded side-effect on this read path. Adds regression tests: - get-credentials: asserts the response exposes only masked metadata and never leaks the access/refresh token. - revoke: locks in that revokeMcpOauthTokens routes OAuth discovery and RFC 7009 revocation through the SSRF-guarded fetch (no raw fetch to an attacker-controlled revocation_endpoint). * fix(webhooks): verify X-Twilio-Signature on Twilio SMS webhooks The twilio (SMS) provider handler implemented no verifyAuth, so the webhook dispatcher queued workflow executions for any request to a known SMS trigger path without validating the Twilio signature — allowing forged inbound SMS events. Only the twilio-voice handler performed signature verification. Extract the shared HMAC-SHA1 signature validation into twilio-signature.ts and wire it into both the SMS and Voice handlers. Verification is enforced when an auth token is configured (parity with Voice); requests without a configured token pass through per the provider-wide optional-secret convention. Add regression tests for both handlers. * fix(connectors): route user-controlled connector hosts through DNS-validated, IP-pinned fetch Knowledge connectors that accept a custom service host/endpoint (S3-compatible endpoints, self-managed GitLab/Sentry hosts, Obsidian vault URLs) performed server-side fetches without the repository's SSRF guard, letting an authenticated user with KB write access probe internal/loopback hosts from the backend. Add secureFetchWithRetry (validateUrlWithDNS + secureFetchWithPinnedIP + the same retry/backoff as fetchWithRetry) and route every request in the s3, gitlab, sentry, and obsidian connectors through it - including pagination and hydration. Gate the S3 plain-http loopback exception to self-hosted deployments. * fix(audit-logs): scope enterprise audit log access to organization boundary Actor membership was used as a standalone tenant predicate, letting org admins read members' audit activity from personal workspaces and other tenants. Scope queries to org-attached workspaces plus org-level events, with actor membership only narrowing the scope; validate workspaceId filters against the caller's organization. * fix(webhooks): warn when Twilio webhook has no auth token configured Addresses PR review: when no auth token is set, verifyTwilioAuth skips signature verification (optional-secret convention). Log a warning so operators can detect a webhook running unauthenticated. * fix(audit-logs): include system events (null actor) in default org audit scope SQL IN never matches NULL, so system/automated events inside org workspaces were hidden unless includeDeparted=true. The default scope now matches current members OR null-actor rows, still inside the org boundary. * fix(auth): type-safe access to OAuth2Tokens raw payload Installed better-auth's OAuth2Tokens no longer declares the raw property; access it through an intersection cast (no behavior change) so type-check passes. * fix(build): keep connector SSRF fetch out of the client bundle The connectors SSRF fix routed s3/gitlab/sentry/obsidian through secureFetchWithRetry, which transitively imports input-validation.server (and its Node-only `dns/promises`). connectors/registry.ts is imported by client components for connector metadata, so the connector sync code — which only ever runs in server API routes — gets pulled into the client bundle, and Turbopack fails to resolve `dns/promises` (no browser shim). - Move secureFetchWithRetry into a dedicated `secure-fetch.server` module so the shared documents/utils stays client-safe; connectors import from there. - Add a browser-only `turbopack.resolveAlias` stub for `dns`/`dns/promises` (the documented Next 16 remedy). Server bundles keep the real module, so SSRF validation is unaffected — only the never-executed client copy is stubbed. Verified with a full `next build` (compiles successfully, no module errors). |
||
|
|
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 |
||
|
|
800f56f7b9 |
fix(clickhouse): harden read-only query enforcement and centralize WHERE-clause validation (#4895)
* fix(clickhouse): centralize WHERE-clause validation in input-validation and harden tautology detection * fix(clickhouse): enforce server-side readonly=1 on the query operation * fix(clickhouse): allow BETWEEN bounds in WHERE validation (OR-only literal rule) and dedupe JSDoc |
||
|
|
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.
|
||
|
|
f7f7840c6c |
fix(otel): make service.instance.id unique per process (#4891)
All app replicas shared a hardcoded service.instance.id ("mothership-sim"),
so OTel metrics from every process collapsed into one Prometheus series.
Their independent cumulative counters then interleaved, producing phantom
counter resets that corrupt rate()/increase() — staging hosted-key cost
inflated to ~$0.72 from a few cents, while no-`key` metrics (cost_charged,
throttled, queue_wait_*) were affected fleet-wide.
Append the hostname (the container id under ECS, unique per task) so each
replica gets its own series and sum(rate(...)) / sum(increase(...)) aggregate
correctly. The mothership-sim prefix is kept so Jaeger's clock-skew adjuster
still separates Sim from Go.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
ce7ddd182f |
feat(tables): workflow version selection (live/deployed) and not-found/no-output badges (#4889)
* feat(tables): workflow version selection (live/deployed) and not-found/no-output badges * fix(tables): draw row-selection left edge as checkbox cell border so it cannot be cut off * fix(tables): per-group version in cascade, accurate deploy error, skip not-found for deployed groups * fix(tables): render selection left edge as continuous strip overlapping row gridlines * feat(tables): not-found column icon, optional workflow inputs, mothership deploymentMode --------- Co-authored-by: waleed <walif6@gmail.com> |
||
|
|
ddab1aaa1c |
refactor(tables): consolidate row data-access in service.ts (#4881)
- Route both row-GET endpoints (internal + v1) and the copilot tool through the single service.queryRows instead of three inline query copies; add a withExecutions option so the public v1 route still omits executions. - Run COUNT(*) and the page fetch concurrently in queryRows. - Move CSV-import transaction ownership out of the API route into importAppendRows / importReplaceRows so routes never hold a trx. - Extract row position mechanics (reserve / shift / compact) into named private helpers in service.ts; no separate table-wrapper module. |
||
|
|
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. |
||
|
|
80ea0ddd7b |
fix(autolayout): relocate notes that overlap blocks after layout (#4888)
* fix(autolayout): relocate notes that overlap blocks after layout * fix(autolayout): harden note overlap resolution against resize and non-finite positions |
||
|
|
16954e46fe |
feat(integrations): add ClickHouse block and expand Dagster + Tinybird tools (#4883)
* feat(integrations): add ClickHouse block and expand Dagster + Tinybird tools
* fix(tinybird): fail loudly on invalid query_pipe parameters JSON
parsePipeParameters previously returned {} on any JSON parse error, so a
mistyped 'parameters' input produced a successful pipe call with the dynamic
filters silently dropped. Throw a clear error for non-empty, non-object input
instead; an omitted/empty value still means 'no parameters'.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(dagster): guard NaN numeric coercions and bound list_assets pagination
Address PR review:
- Route all block numeric coercions (list_runs limit/createdAfter/createdBefore,
get_run_logs logsLimit, list_assets assetsLimit) through a toFiniteNumber()
guard so invalid/wand-generated text becomes undefined instead of NaN.
- list_assets now applies a default page size (100) when no limit is given, so
paging stays bounded and hasMore is meaningful even when limit is omitted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(dagster): make list_assets hasMore exact via fetch N+1
Address PR review (hasMore true on exact page): request one extra row
(pageSize + 1), use its presence as the authoritative hasMore, slice it off,
and derive the returned cursor from the last RETURNED asset's key path
(JSON-serialized; Dagster normalizes JS/Python whitespace on the way in).
This removes the false-positive hasMore when the final page is exactly full.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(clickhouse): enforce read-only query operation and harden WHERE-clause guard
* fix(dagster): make list_runs hasMore exact via fetch N+1
Address PR review (list runs false hasMore): request one extra row
(pageSize + 1), use its presence as the authoritative hasMore, and slice it
off before mapping. Removes the false-positive hasMore (and misleading cursor)
when the final page is exactly `limit` runs long. Mirrors the list_assets fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(clickhouse): restrict DROP PARTITION to literal values to prevent SQL injection
* fix(clickhouse): reject chained statements in read-only query operation
* fix(clickhouse): force JSON output on query path and ignore comments when detecting chained statements
* fix(tinybird): encode datasource/pipe names in URL paths to prevent traversal
A user-or-llm datasource/pipe name interpolated raw into the URL path (e.g.
'real_ds/../../other') is normalized by the WHATWG URL parser and can target a
different endpoint. Wrap the path segment with encodeURIComponent in the
truncate, delete, and query_pipe URLs. Events/append pass the name via
URLSearchParams, which already encodes, so they were unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(clickhouse): block WITH-led writes/DDL in read-only query operation
* fix(clickhouse): validate column types structurally and normalize FORMAT around SETTINGS
* fix(clickhouse): balance-check ORDER BY/PARTITION BY and skip leading comments in read-only guard
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
2ccd9a3ac5 |
feat(connectors): add 7 knowledge base connectors (Google Forms, Typeform, Azure DevOps, YouTube, JSM, S3, Sentry) (#4880)
* feat(connectors): add 7 knowledge base connectors (Google Forms, Typeform, Azure DevOps, YouTube, JSM, S3, Sentry) * fix(connectors): tighten listingCapped semantics per review (WIQL cap, batch omissions, cap-vs-exhaustion) * fix(connectors): google-forms listingCapped must fire on slice regardless of hitLimit (404-null-filter gap) * fix(connectors): s3 streaming size cap for chunked responses without content-length * fix(connectors): ado byte-exact file content fetch, google-forms hash-poisoning on listing failure * fix(connectors): ado auth-failure deletion guard, jsm last-page slice flag, google-forms response cap in hash * fix(connectors): shared streaming size-cap reader for ado file hydration (promote from s3) * fix(knowledge): flag incomplete listings at engine level when pagination is truncated * fix(connectors): ado flags listing incomplete when a non-empty repo has no resolvable branch * fix(knowledge): engine truncation flag is an absolute deletion block (fullSync cannot override); s3 byte-exact size fallback; ado tsdoc accuracy * improvement(knowledge): extract shouldReconcileDeletions gate as tested pure function, tighten engine comments * test(connectors): mapTags coverage for the 7 new connectors * fix(connectors): ado probes past the wiql 20k cap before flagging; document custom-wiql full-listing behavior * fix(connectors): ado flags partial repo trees when items listing emits a continuation token * fix(connectors): ado discards foreign-phase cursors; google-forms scans all response pages for change detection * fix(connectors): audit fixes across new connectors - registry: register x connector (was dead code, never wired in) - google-docs/google-drive/google-forms: gate deletion reconciliation on Drive incompleteSearch; google-docs also now sets listingCapped on its maxDocs cap path - jsm: add read:jira-user scope so reporter resolves on requests - gong: only set listingCapped on genuine truncation, not exact-cap source exhaustion - gitlab: issues phase switched to keyset pagination (removes ~50k offset ceiling), matching the repo-tree phase - grain: parallelize recording + transcript fetch in getDocument - ashby: document updatedAt-based content-hash limitation for notes/feedback change detection - tests: mapTags coverage for x, granola, greenhouse, fathom, rootly |
||
|
|
5505076222 |
chore(db): drop legacy copilot_chats.messages JSONB column (#4886)
Reads and writes are fully cut over to the normalized copilot_messages table (verified in production: no writes to the column in 24h, recently-active chats have empty JSONB while copilot_messages holds the transcript). Drop the dead column via drizzle migration 0225 and re-type CopilotChatDetailRow.messages as an assembled (non-column) field. Deploy notes: reconcile any chats where the JSONB still leads copilot_messages before applying, and pg_repack copilot_chats afterward to reclaim the ~5.7GB TOAST storage (DROP COLUMN is metadata-only). |
||
|
|
71f693645c |
fix(polling-tools): pass plan execution timeout to internal polling tool routes (#4884)
* fix(polling-tools): pass plan execution timeout to internal polling tool routes * address comments |
||
|
|
e761043da7 |
chore(skills): mirror model/enrichment/hosted-key/council skills into .agents/skills and expand add-model touchpoints (#4882)
* chore(skills): mirror model/enrichment/hosted-key/council skills into .agents/skills and expand add-model touchpoints * chore(skills): document council yaml omission and disambiguate validate-model cross-ref |