Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: simstudioai/sim
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: simstudioai/sim
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: staging
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 19 commits
  • 1,526 files changed
  • 10 contributors

Commits on Jun 6, 2026

  1. improvement(platform): remove tour, simplify sidebar/header, drop loa…

    …ding skeletons (#4354)
    
    * improvement(platform): workspace UI/UX overhaul + integrations catalog
    
    Rework the workspace around the AI-workspace model: a Mothership home, a
    top-level Skills route, connected-credential and integration-detail pages,
    and a polished sidebar/settings surface. Replace the notifications store
    with a unified toast system (provider-level dismiss/pause, countdown ring).
    
    Integrations & catalog:
    - Add a BlockMeta layer (tags + catalog templates) scoped to catalog-visible
      integrations; every catalog integration carries >=7 grounded templates.
    - Rework the taxonomy: each block declares category tools|blocks|triggers.
      3rd-party services are 'tools'; first-party primitives (postgres, mysql,
      knowledge, file, search, stt/tts, image/video generators, thinking, etc.)
      are 'blocks'. Versioned blocks follow the upgrade paradigm (old hidden,
      latest in toolbar/docs).
    - Generate integrations.json + tool docs canonically from block configs.
    
    Architecture & cleanup:
    - Consolidate block data extraction behind a single latest-version strategy
      (getCanonicalBlocksByCategory; version-consistent getBlockMeta).
    - Unify version-suffix handling in @sim/utils/string (stripVersionSuffix /
      isVersionedType, with tests); registry, generate-docs, tools/utils, and
      integrations all route through it.
    - Repair latent broken barrels, remove dead code, fix BlockMeta-related type
      errors and 5 broken docs links.
    
    Behavior-preserving for block execution and the toolbar's tool/block listing.
    
    * refactor(platform): remove forms, templates, and creators features
    
    Remove three standalone features and their supporting code:
    - Forms: form-deployment pages, API routes, execution path, and docs.
    - Templates: the template gallery (landing + workspace) and template APIs.
    - Creators: creator-profile routes and contracts.
    
    Add a super-user permissions module (lib/permissions/super-user) and an
    organizations API contract; update the audit/db/testing packages, billing,
    and the session/theme providers accordingly.
    
    * test(workflows): update archiveWorkflow update count after forms removal
    
    The forms feature was removed, dropping the form-table update from
    archiveWorkflow. Update the stale assertion from 8 to 7 tx.update calls.
    
    * upgrade
    
    * improvement(knowledge): polish tag filter dropdowns (#4816)
    
    * improvement(logs): object storage backed tracespans (#4787)
    
    * improvement(logs): obj storage backed tracespans
    
    * fix storage write context
    
    * fix tests
    
    * address comments
    
    * address comments
    
    * chore(db): remove migration 0219 to regenerate after staging merge
    
    Drops the 0219_robust_shard SQL, its snapshot, and the journal entry so the
    trace-spans/cost schema migration can be regenerated on top of the latest
    staging migration chain (avoids a number collision with staging's migrations).
    
    Co-authored-by: Cursor <cursoragent@cursor.com>
    
    * improvement(billing): accurate per-member usage via shared ledger helper
    
    Per-member/per-user usage in the org-member routes now adds the usage_log
    ledger to the currentPeriodCost baseline (which is no longer incremented),
    via a shared getOrgMemberLedgerByUser helper to avoid repeating the
    subscription→period→ledger lookup across the admin and member-facing routes.
    
    Co-authored-by: Cursor <cursoragent@cursor.com>
    
    * regen migrations
    
    * update migration
    
    * address comments
    
    * more code cleanup
    
    * incorrect type cast
    
    ---------
    
    Co-authored-by: Cursor <cursoragent@cursor.com>
    
    * improvement(providers): harden OpenAI-compatible providers + add tests (#4796)
    
    * improvement(providers): harden OpenAI-compatible providers + add tests
    
    * fix(vllm): let tool-loop errors propagate instead of returning silent partial success
    
    * fix(litellm): force tool_choice 'none' on final structured-output call
    
    The deferred final call used tool_choice 'auto', so the model could emit
    another tool_calls round instead of the structured answer, leaving content
    stale. Use 'none' (matching vLLM/Fireworks) on both the streaming and
    non-streaming final calls so the model must return the structured response.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(providers/ollama): drop tools from post-tool streaming call
    
    Ollama ignores tool_choice (not in its supported fields), so vLLM/Fireworks'
    tool_choice:'none' guard is a no-op here. Omit tools from the final streaming
    payload instead so the summarization turn can't emit dropped tool calls.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(litellm): spread payload into deferred final call so reasoning_effort carries over
    
    The non-streaming deferred finalPayload hand-picked fields and dropped
    reasoning_effort (and any future payload field), diverging from the streaming
    path which spreads ...payload. Spread payload here too for consistency.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * chore(providers/ollama): restore enrichment TSDoc block
    
    Keeps parity with sibling Chat Completions providers (cerebras/mistral/xai).
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * docs(fireworks): restore TSDoc on utils helpers
    
    Restore the TSDoc blocks on supportsNativeStructuredOutputs,
    createReadableStreamFromOpenAIStream, and checkForForcedToolUsage —
    TSDoc is the codebase documentation standard and should not have been
    stripped.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * chore(litellm): remove inline rationale comments (codebase uses TSDoc)
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * chore(providers/ollama): drop orphaned enrichment TSDoc
    
    The block documented a function that now lives in trace-enrichment.ts, so it
    documents nothing in this file.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    
    * chore(copilot): deprecate mcp server (#4797)
    
    * chore(copilot): deprecate mcp
    
    * update error codes
    
    * deprecate copilot api v1 route
    
    * feat(integrations): hosted API keys for Findymail, Prospeo, and Wiza (#4777)
    
    * feat(integrations): hosted API keys for Findymail, Prospeo, and Wiza
    
    Add hosted-key support across all credit-consuming Findymail, Prospeo, and Wiza operations so Sim provides the key when a workspace has not brought its own. Register the three BYOK providers, consolidate Wiza's two-step reveal into a single polling wiza_individual_reveal op, and hide the API key field on hosted Sim for hosted operations.
    
    * fix(integrations): harden Wiza reveal polling, soften enrichment getCost guards
    
    Address Greptile + Cursor Bugbot review on #4777: return explicit failures from the Wiza individual_reveal poller instead of throwing (thrown errors were swallowed into a false queued success), short-circuit when the initial reveal is already terminal, tolerate transient 5xx/429 during polling, and return 0 (not throw) from Findymail getCost when the contacts/employees array is absent.
    
    * chore(integrations): biome formatting after wiza merge resolution
    
    * fix(wiza): type isTerminalReveal param structurally for next build typecheck
    
    * feat(enrichments): add Findymail, Prospeo, Wiza to work-email waterfall
    
    * feat(enrichments): add Wiza + Prospeo phone reveal to phone-number waterfall
    
    * feat(enrichments): opportunistic identifiers + LinkedIn URL input across work-email & phone cascades
    
    * fix(tables): reduce column header chevron size and fix sidebar shadow bleed (#4800)
    
    * feat(slack): add install + privacy section to integration landing page (#4799)
    
    * feat(slack): add install + privacy section to integration landing page
    
    Adds a hand-authored, slug-keyed landing-content module (separate from the generated integrations.json so it survives regeneration) and renders an install walkthrough + privacy-policy link on integration pages when present. Also refreshes generated docs (data-enrichment entry, icon mappings, tool mdx).
    
    * fix(landing): render privacy section independently, align CTA analytics label
    
    * docs(landing): clarify the Slack install button is behind sign-in
    
    * refactor(landing): bake integration landing content into generated json via docs-gen
    
    Moves landing content (install walkthrough + privacy) out of a render-time augment and into the generation pipeline: generate-docs reads the pure-data content map and writes landingContent into integrations.json, so the page reads a single source (integration.landingContent). Canonical types live in integrations/data/types.ts.
    
    * improvement(enrichments): align enrichments sidebar with design system (#4801)
    
    * improvement(enrichments): align enrichments sidebar with design system
    
    * fix(enrichments): consistent close button pattern and fix url link hover
    
    * fix(misc): upgrade path change for new better-auth version, billing issue for workflow block agent usage (#4803)
    
    * fix(misc): upgrade path change for new better-auth version, double-billing for workflow block agent usage
    
    * fail loudly if stripe sub id missing
    
    * fix(copilot): seq migration (#4804)
    
    * chore(db): drop redundant idx_webhook_on_workflow_id_block_id index (#4809)
    
    Removed because (workflow_id, block_id) is a left-prefix of idx_webhook_on_workflow_id_block_id_updated_at_desc, which fully covers it. The dropped index was non-unique and enforced no constraint.
    
    * perf(copilot): read chat transcripts from copilot_messages (R+1 cutover) (#4808)
    
    * perf(copilot): read chat transcripts from copilot_messages, not JSONB
    
    Flip user-facing chat reads from the legacy copilot_chats.messages JSONB
    array (5.7GB, 99% TOAST) to the normalized copilot_messages table via a
    new loadCopilotChatMessages helper ordered by seq NULLS LAST, created_at,
    id — the verified canonical order. Both chat-detail getters
    (getAccessibleCopilotChat, getAccessibleCopilotChatWithMessages) now drop
    the messages column from their metadata select (no more whole-array
    detoast on every load) and assemble the transcript from the table after
    authorization. This cascades to the copilot + mothership GET endpoints
    and to resolveOrCreateChat's conversationHistory (the LLM payload).
    
    The normalize/effective-transcript pipeline is source-agnostic
    (copilot_messages.content == a JSONB array element), so transcripts are
    byte-identical. Dual-write and the JSONB column stay in place as the
    internal-logic source and fallback; removing JSONB writes is a later step.
    
    Prod integrity verified before cutover: 0 messages missing, 0 NULL-seq,
    0 dup keys/seq, 0 orphans, order-parity vs JSONB = 0 mismatches.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * test(copilot): cover auth-deny on a found row skips the messages query
    
    Address PR review: exercise the `if (!authorized) return null` contract —
    when the chat row exists but authorization fails, the getter returns null
    and never issues the copilot_messages read.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(tables): right-align run/stop in embedded toolbar; workflow cells format like normal cells (#4806)
    
    * fix(tables): right-align run/stop in the embedded table toolbar
    
    Add a right-aligned `trailing` slot to ResourceOptionsBar and move the embedded
    mothership table's run/stop control into it, so Filter + Sort stay left-aligned
    and run/stop sits opposite on the right. No-op for the search-bearing consumers
    (logs, resource list), which don't pass `trailing`.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    * fix(tables): workflow-output cells format values like normal cells
    
    Workflow-output columns short-circuited in resolveCellRender and rendered their
    value as plain text, so a sim-resource URL / external URL / JSON / date produced
    by a workflow never got the chip, favicon link, or typed formatting a normal
    cell gets. Factor value formatting into a shared `resolveValueKind` helper used
    by both the workflow-value branch and the plain-cell branch; the workflow branch
    keeps the typewriter reveal for plain streaming text via a `typewriter` flag.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    * fix(tables): detect resource/URL links on workflow output regardless of column type
    
    Workflow output columns default to `json` (columnTypeForLeaf), so routing their
    values through the type-based formatter (a) gated chip/URL promotion behind
    `column.type === 'string'` — a URL produced by a json-typed output never became
    a chip — and (b) JSON.stringify'd plain string values, adding quotes and losing
    the typewriter reveal. Detect links (sim-resource chip / favicon URL) on the
    value string directly for workflow outputs, falling back to the plain `value`
    kind; plain cells keep the type-based formatting. Addresses Greptile P2 on #4806.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    * fix(icons): repair broken integration icon rendering (#4810)
    
    * fix(icons): repair broken integration icon rendering
    
    Two distinct bugs left integration icons broken on the /integrations page
    (visible at 32-40px, hidden at the toolbar's 16px):
    
    1. Corrupted SVG paths (Notion, Greptile, Granola, Calendly, Grafana, Bedrock):
       over-minified data dropped elliptical-arc flag digits (e.g. `A1 1 0 5.9 7`
       instead of `A1 1 0 0 0 5.9 7`); Granola's cubic stream was truncated. Browsers
       abort path parsing at the first invalid arc flag, so each rendered as a fragment
       or blank. Replaced with correct path data from canonical sources, preserving each
       icon's existing fill/gradient and bgColor.
    
    2. Invisible glyph (Bright Data): its icon uses fill='currentColor' but bgColor was
       '#FFFFFF', and every surface forces text-white on the glyph - white-on-white.
       Changed bgColor to Bright Data's brand blue (#3d7ffc) so the white glyph reads,
       matching the white-glyph-on-brand-chip convention.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(icons): restore Calendly dual-tone brand colors
    
    Addresses review feedback: the previous fix replaced the broken Calendly icon
    with a monochrome #006BFF path, dropping the cyan #0ae8f0 accent from the
    original dual-tone mark. Restored the two-tone logo (blue + cyan) using clean,
    valid path data, cropped to a tight square viewBox so it fills the chip.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * improvement(icons): enlarge icons, fix Zoom contrast and Quiver chip
    
    - Zoom: glyph was blue-on-blue (#0B5CFF on #2D8CFF chip); switched to
      currentColor so it renders as a white glyph on the blue chip.
    - Quiver: chip bgColor #000000 -> #FFFFFF to match the icon's near-white box,
      and enlarged the mark slightly (viewBox crop).
    - Enlarged (tightened viewBox, verified no clipping): RevenueCat, Prospeo,
      Granola, Firecrawl, Enrich.so, and the AWS icons (RDS, DynamoDB, SQS,
      CloudFormation, Athena, CloudWatch, SES, Bedrock, S3).
    - ZoomInfo left unchanged: it is a full red rounded-square logo that already
      fills its frame, so a crop would clip it.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(icons): use Bright Data wordmark on white chip; repair Circleback
    
    - Bright Data: replaced the flame glyph with the official two-tone 'bright data'
      wordmark (provided asset), centered in a symmetric viewBox. Reverted the chip
      bgColor from #3d7ffc to #FFFFFF since the blue wordmark is invisible on a blue
      chip (the wordmark is designed for a light background).
    - Circleback: a minifier had rounded the pattern's image scale to scale(0),
      collapsing the embedded logo to zero size (invisible). Restored the correct
      scale (1/280 = 0.00357142857) so the C. mark renders.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(docs): sync Quiver block color card to white chip
    
    Reflects the Quiver bgColor change (#000000 -> #FFFFFF) in the docs block info card.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * improvement(icons): enlarge AWS/Cloudflare/Dagster icons, fully white Zoom
    
    - Enlarged (tighter viewBox, render-verified, no clipping): Cloudflare, Dagster,
      and the red AWS icons AWS IAM, Identity Center, Secrets Manager, SES, STS.
      Identity Center was anomalously small (filled ~32% of its frame); the group is
      now sized consistently (~80% fill).
    - Zoom: the camera lens triangle was still #0B5CFF (blue-on-blue); switched it to
      currentColor so the whole camera renders white on the blue chip.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * docs(wiza): consolidate individual reveal into a single operation
    
    Merges the separate Start/Get Individual Reveal operations into one Individual
    Reveal operation in the Wiza docs and integrations data (operationCount 5 -> 4).
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * improvement(icons): size remaining AWS icons to match the set (~80% fill)
    
    Bring RDS, DynamoDB, SQS, CloudFormation, Athena, CloudWatch and S3 up to the
    same ~80% fill as the AWS IAM/Identity Center/Secrets Manager/SES/STS group, so
    all AWS icons are visually consistent. Bedrock left as-is (already ~92% fill).
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(icons): use Bright Data flame mark, enlarge ZoomInfo
    
    - Bright Data: the full 'bright data' wordmark was illegible at chip size.
      Replaced with just the flame-'i' brand mark (blue #4280f6 on the white chip),
      centered.
    - ZoomInfo: cropped the viewBox toward the white 'Zi' so it's larger; the red
      rounded-square background still fills the chip.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * improvement(icons): enlarge CrowdStrike icon
    
    The falcon mark sat small in its chip because the icon used a wide 768x500
    viewBox (letterboxed in the square chip). Switched to a square viewBox centered
    on the mark so it fills ~80%, consistent with the other icons.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(tables): serialize schema mutations to prevent parallel column clobber (#4812)
    
    * Make workflow description nullable
    
    * fix(tables): serialize schema mutations to prevent parallel column clobber
    
    * fix(tables): load workflow outside schema lock; use DbOrTx for getTableById
    
    * fix(tables): scale idle timeout in updateColumnType to avoid aborting large type changes
    
    * fix(tables): skip stale remap types when workflowId changes concurrently
    
    * fix(tables): scale idle timeout in updateColumnConstraints for large tables
    
    * fix(wait): resume live/draft async waits and preserve cell context on chained waits (#4814)
    
    * Make workflow description nullable
    
    * fix(wait): resume live/draft async waits and preserve cell context on chained waits
    
    * improvement(knowledge): polish tag filter dropdowns
    
    * improvement(knowledge): soften filter section labels
    
    * improvement(knowledge): soften list filter labels
    
    * fix(security): harden SSO domain registration, webhook path isolation, and CSV export (#4813)
    
    * fix(security): harden KB file access, SSO domain registration, webhook path isolation, env secrets, and CSV export
    
    * fix(sso): scope domain conflict query with indexed lower(domain) filter
    
    Address PR review: avoid a full-table scan on every SSO provider
    registration by filtering candidate rows in SQL with
    lower(domain) = <normalized>, keeping the in-memory ownership check.
    Also tighten the normalizeSSODomain TSDoc.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * chore: condense env route security comments
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * icons update
    
    * chore(security): tighten inline comments in CSV export and KB file authorization
    
    Condense verbose comment blocks to concise TSDoc/single-line form; no behavior change.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(security): validate internal serve origin in KB file authorization
    
    Replace the bypassable isInternalFileUrl substring check in resolveInternalKbKey
    with an origin allow-list (base URL, internal API base URL, TRUSTED_ORIGINS).
    A crafted external host whose path is /api/files/serve/<victim-key> no longer
    resolves to the victim key. Relative same-origin URLs are unaffected.
    
    * style(sso): use idiomatic sql lower() comparison for domain conflict query
    
    Match the repo's prevailing `sql`lower(col) = value`` idiom for the
    case-insensitive SSO domain conflict lookup.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(security): align workspace env admin gate with hasWorkspaceAdminAccess
    
    Use the same admin check the secrets UI uses (owner, admin permission, or
    org-admin) so owners and org-admins are not wrongly denied their own decrypted
    workspace secrets, while read-only members remain restricted to names only.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(sso): rely on lower(domain) match for conflict detection, drop dead in-memory recheck
    
    Address PR review: the SQL `lower(domain) = <normalized>` predicate already
    excludes rows that the in-memory `normalizeSSODomain(...) === domain` recheck
    claimed to catch, making that recheck dead/misleading code. Match on the
    canonical lower-cased domain and filter purely by ownership. Malformed legacy
    values (wildcards, schemes, ports) never match an email domain at sign-in, so
    excluding them is not a gap. Test DB mock now applies the lower() predicate so
    the casing-variant case is genuinely exercised.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(security): scope webhook deploy path conflict to active webhooks
    
    findConflictingWebhookPathOwner omitted the isActive filter that the
    runtime dispatcher (findAllWebhooksForPath) applies, so an inactive but
    non-archived webhook from another workflow (e.g. after undeploy or
    failure auto-disable) would permanently block any new deployment on that
    path even though it never receives deliveries. Align the guard with the
    runtime isActive + archivedAt filter; the earliest-owner runtime check
    remains the authoritative cross-tenant protection. Also trims verbose
    TSDoc on the webhook path-isolation helpers.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(security): exclude archived workflows from webhook deploy path conflict
    
    findConflictingWebhookPathOwner now joins workflow and filters
    isNull(workflow.archivedAt), matching the runtime dispatcher
    (findAllWebhooksForPath). A webhook on an archived workflow can never
    receive deliveries at runtime, so it must not block legitimate path reuse
    with a 409.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(security): anchor KB file ownership to earliest document in any state
    
    A KB file's owner is now the earliest document referencing its key regardless of
    state (active/archived/deleted/excluded); access is granted only when that owning
    document is still active. Closes the residual where an attacker could plant an
    active document to claim a file whose original document was archived or deleted.
    
    * updated greptile icon
    
    * revert(security): drop KB file authorization changes
    
    Reverts the knowledge-base file-access work (origin-pinning / owner-pinning /
    origin allow-list in verifyKBFileAccess) and its test. The other hardening fixes
    (SSO domain registration, webhook path isolation, workspace env secrets, CSV
    export) are unchanged. apps/sim/app/api/files/authorization.ts is restored to its
    origin/staging baseline.
    
    * fix(sso): treat caller's own user-scoped provider as owned during conflict check
    
    Self-hosters often register SSO user-scoped via the CLI script (no
    SSO_ORGANIZATION_ID). If they later enable organizations and reconfigure the
    same domain org-scoped through the UI, the conflict check previously treated
    their own user-scoped row as another tenant's and returned a misleading 409.
    Recognize the caller's own user-scoped provider as owned so that migration is
    allowed, while still blocking another user's or another org's domain.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * revert(security): remove workspace-env admin gate
    
    Defer to a credential-based access model (separate change). Restores
    GET /api/workspaces/[id]/environment to main behavior and removes the test.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * refactor(security): consolidate webhook path-collision check into one helper
    
    Extract findConflictingWebhookPathOwner to lib/webhooks/utils.server.ts as
    the single source of truth for cross-tenant path-collision detection, used by
    both webhook creation paths (deploy sync and the manual /api/webhooks route).
    
    This also repairs two latent issues in the manual route's previous inline
    check, which queried with limit(1) and only webhook.archivedAt:
    - limit(1) inspected one arbitrary row, so a same-workflow row could mask a
      foreign collision (false negative). The shared helper scans all matching
      rows.
    - It omitted isActive/workflow.archivedAt, so inactive or archived-workflow
      webhooks (which never receive deliveries) permanently blocked path reuse.
      The helper mirrors the runtime dispatcher's filter.
    
    Same-workflow webhook reuse for upsert is now a separate, explicit lookup.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(security): block private/reserved IPs for hosted 1Password Connect SSRF (#4818)
    
    * fix(security): block private/reserved IPs for hosted 1Password Connect SSRF
    
    * test(security): use real isPrivateOrReservedIP and cover IPv6 edge cases
    
    * improvement(integrations): validate and expand devin, cursor, and greptile (#4820)
    
    * improvement(integrations): validate and expand devin, cursor, and greptile
    
    - devin: fix missing org_id path segment on all session endpoints, add 7 session sub-resource tools (list messages/attachments, get/append/replace tags, archive, terminate), pagination, and is_archived output
    - cursor: add get_api_key_info, list_models, list_repositories tools
    - greptile: align block and docs
    - normalize array outputs to default [] and tighten types
    
    * refactor(cursor): simplify list_repositories v2 array normalization
    
    Collapse the redundant `?? []` + `Array.isArray` double-guard into a
    single Array.isArray check, per PR review feedback.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(devin): scope session-tag mapping to tag ops and normalize array tag inputs
    
    - Only map sessionTags into the tools tags param for append/replace operations,
      preventing stale sessionTags state from clobbering create_session tags
    - Fall back to a wired tags value when sessionTags is empty for tag operations
    - Normalize tag inputs (string or wired string[]) via normalizeTags so array
      values from other blocks no longer throw on .split
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(cursor): restore base64 file data in legacy download_artifact metadata
    
    The legacy CursorBlock exposes only content + metadata (no v2 file
    output), so metadata.data was the only way legacy-block workflows could
    access downloaded artifact bytes. Restore the base64 data field and
    document it in the outputs/type instead of dropping it.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(devin): coerce terminateArchive to archive flag for boolean-wired input
    
    * docs(integrations): regenerate tool docs for new devin and cursor operations
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(search-replace): don't auto-navigate when content edits invalidate the active match (#4819)
    
    * fix(search-replace): don't auto-navigate when content edits invalidate the active match
    
    * fix(search-replace): clear afterReplaceIndexRef on apply failure and zero matches
    
    * fix(search-replace): remove duplicate setActiveSearchTarget(null) on close
    
    * fix(search-replace): move afterReplaceIndexRef write inside handleApply past the guard
    
    * fix(search-replace): auto-navigate when hydration resolves with no prior active match
    
    * chore(search-replace): remove inline comments
    
    * fix(search-replace): revert !activeMatchId guard that caused immediate re-navigation after deselect
    
    * improvement(enrichments): limit company-info to fields both providers return (#4817)
    
    Hunter's company dataset returns null industry/foundedYear for many large companies (verified against the live API for Microsoft, Amazon, Google), so under the first-non-empty-wins cascade those columns appeared inconsistently across rows. Limit company-info outputs to employee count and description — the fields Hunter and PDL both reliably return — so every row is consistent. employeeCount is a string so Hunter's range bucket and PDL's exact count share the column.
    
    * fix(files): don't reject external URLs containing '..' in file parse validation (#4821)
    
    * fix(files): don't reject external URLs containing '..' in file parse validation
    
    The file block's file_fetch operation rejected any external URL whose path
    contained '..' (e.g. Slack files-pri slugs with a literal '...') with
    'Access denied: path traversal detected'. Traversal checks only apply to
    local paths — external http(s) URLs are fetched with SSRF protection
    downstream and are never resolved against the filesystem, so they now
    short-circuit as valid. Internal /api/files/serve/ URLs keep full traversal
    protection.
    
    * test(files): fix external-URL assertion to handle undefined error
    
    * test(files): assert success explicitly in external-URL traversal test
    
    * fix(files): keep traversal protection for https URLs matching internal serve paths
    
    * feat(google-sheets): add row filtering to read with numeric operators (#4822)
    
    * feat(google-sheets): add row filtering to read with numeric operators
    
    Adds client-side row filtering to the Google Sheets read (v2) operation.
    Filter the returned rows by a header column using text operators
    (contains, not_contains, exact, not_equals, starts_with, ends_with) and
    numeric/ordering operators (gt, gte, lt, lte). Filtering lives in a pure,
    unit-tested helper (filterSheetRows) and runs over the fetched read range;
    an optional `filter` output reports whether the column was found and how
    many rows matched.
    
    Also hardens the surrounding tools:
    - trim spreadsheetId in write/update/append URL builders (matches read)
    - URL-encode the v1 read default range
    - expose valueInputOption for the update operation in the block
    
    Backwards compatible: with no filter requested, read output is byte-
    identical and the `filter` field is omitted. The filterMatchType union is
    widened additively (4 -> 10 values).
    
    * fix(google-sheets): correct filter metadata for missing column and header-only sheets
    
    - matchedRows is now 0 (not totalRows) when the filter column is not found,
      so it no longer contradicts applied=false / columnFound=false
    - columnFound now reflects an actual header lookup for empty/header-only
      sheets instead of being hardcoded true
    - add tests covering header-only and empty sheets with present/absent columns
    
    * fix(selectors): fetch all pages for paginated dropdown list routes (#4823)
    
    * fix(selectors): fetch all pages for paginated dropdown list routes
    
    Dropdown selectors fetched only the first page of paginated provider
    APIs, silently hiding results past page one. Add bounded server-side
    draining to the list routes across Microsoft Graph, Google, Notion,
    Atlassian, Linear, AWS CloudWatch, and offset/token REST APIs, plus a
    shared client-side drain cap in the selector hook. Response shapes,
    stored values, and tool execution are unchanged; CloudWatch list tools
    still honor a caller-supplied limit. Also fixes the Word file picker
    that was searching for .xlsx files.
    
    * fix(selectors): harden JSM and Monday pagination draining
    
    - JSM service-desk/request-type drains advance `start` by the actual row
      count returned (not the fixed page size) and stop on an empty page, so a
      short non-final page can't skip items.
    - Monday boards drain now checks `response.ok` per page, surfacing a
      mid-drain HTTP failure instead of treating it as an empty final page and
      returning a partial 200.
    
    * docs(selectors): clarify JSM drain advances start by actual row count
    
    The offset-advancement fix (advance `start` by the rows returned, not the
    fixed page size) landed in 7b19788a8; update the TSDoc to match so it no
    longer reads as advancing by `limit`.
    
    * fix(selectors): drain fetchPage in direct fetchList callers
    
    Making `fetchList` optional left three direct callers (outside the
    useSelectorOptions hook) calling it unguarded, which broke the build's
    type check. Route them through a shared `loadAllSelectorOptions` helper
    that uses `fetchList` when present and otherwise drains `fetchPage`.
    This also prevents a regression: `confluence.spaces` / `knowledge.documents`
    now paginate via `fetchPage` only, and these callers (search/replace,
    value resolution) would otherwise have silently returned no options.
    
    * chore(selectors): rename MAX_PAGE_PAGES to MAX_NOTION_PAGES for readability
    
    * fix(sso): re-check domain conflict before write and reject IP-address domains (#4825)
    
    * improvement(copilot): make copilot_messages the sole transcript store, remove JSONB dual-write (#4826)
    
    Stop writing/reading the legacy copilot_chats.messages JSONB column now that
    reads are cut over to copilot_messages. Make appendCopilotChatMessages the
    primary write (throws on failure instead of swallowing), repoint peripheral
    readers (workspace VFS, chat cleanup, data drains, fork, superuser import) to
    copilot_messages, and persist the assistant turn inside finalizeAssistantTurn's
    transaction so it commits atomically with the stream-marker clear. The column
    itself is dropped in a follow-up migration after this bakes.
    
    * feat(tables): expand filter operators (not-contains, starts/ends-with, not-in, empty) (#4827)
    
    Add does-not-contain ($ncontains), starts-with ($startsWith), ends-with
    ($endsWith), not-in-array ($nin, previously executed server-side but unexposed
    in the UI), and is-empty/is-not-empty ($empty) filter operators end-to-end —
    SQL builder, condition types, query-builder converters/constants, the filter
    UI, the Table tools/block descriptions, and docs.
    
    Also fix correctness bugs in the filter builder surfaced by the wider operator
    set:
    - Same-column AND rules (e.g. age > 18 AND age < 65, or name startsWith 'A'
      AND name endsWith 'Z') silently overwrote each other because the AND group
      was keyed by column name. They now merge into one operator object, which
      also makes Filter -> rules -> Filter round-trip losslessly for multi-operator
      columns.
    - $nin values were not split into an array like $in, and textual-match values
      like "123" were numeric-coerced (breaking the ILIKE path).
    - A non-boolean $empty operand from the raw API silently inverted the check; it
      now coerces 'true'/'false' strings and otherwise returns a 400.
    
    * improvement(copilot): stop persisting tool-call result outputs in transcripts (#4829)
    
    Opening a Mothership task could take many seconds because a single persisted
    assistant message in copilot_messages.content can reach hundreds of MB, almost
    entirely inside contentBlocks[].toolCall.result.output (e.g. a get_workflow_logs
    or run_workflow result). The DB query is ~2ms; the cost is detoasting that
    payload, shipping it to the browser, and parsing it.
    
    These outputs are dead weight on the Sim side: they are never rendered (the
    thread shows only tool name/title/status) and never replayed to the model (the
    upstream copilot service owns conversation memory). So drop result.output before
    it is persisted, keeping result.success/error plus the tool metadata.
    
    - add stripToolResultOutput() in persisted-message.ts
    - apply it in messages-store toRow (covers every write path) and in
      loadCopilotChatMessages (existing rows render fast on read)
    
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    
    * feat(providers): add Together AI, Baseten, and Ollama Cloud model providers (#4830)
    
    * feat(providers): add Together AI, Baseten, and Ollama Cloud model providers
    
    * fix(providers): guard Ollama streaming fast-path with hasActiveTools
    
    Match Together/Baseten/Fireworks: when tools are supplied but all are
    filtered out (usageControl 'none'), take the single streaming call instead
    of an extra non-streaming round-trip.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(providers): filter non-chat model types from Together model list
    
    * refactor(providers): dedupe Ollama Cloud upstream schema
    
    ollamaCloudUpstreamResponseSchema was byte-for-byte identical to
    ollamaUpstreamResponseSchema (both /api/tags endpoints return the same
    { models: [{ name }] } shape). Drop the duplicate and reuse the shared schema.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    
    * fix(knowledge): calendar view sync, deduplicate popover animation classes, type-safe filter cast
    
    * cleanup(knowledge): remove TRIGGER_BORDER_CLASS duplication, inline displayLabel, drop enabledFilterParam alias
    
    ---------
    
    Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
    Co-authored-by: Cursor <cursoragent@cursor.com>
    Co-authored-by: Waleed <walif6@gmail.com>
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    Co-authored-by: Theodore Li <theo@sim.ai>
    Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com>
    
    * feat(blocks): add BlockMeta to Quiver and Linq; fix invalid block config fields; update skills
    
    Block fixes:
    - Add QuiverBlockMeta (tags + 3 templates: icon generator, diagram creator, vectorizer)
    - Fix QuiverBlock: remove invalid tags field from BlockConfig, IntegrationType.Design →
      IntegrationType.AI (Design doesn't exist in the enum)
    - Fix GreptileBlock: remove invalid tags field from BlockConfig,
      IntegrationType.DeveloperTools → IntegrationType.DevOps
    - Fix LinqBlock: remove invalid tags field from BlockConfig (tags belong only in BlockMeta)
    
    Skills:
    - add-block: add dedicated BlockMeta section with structure, rules, and registration
      pattern; add BlockMeta checklist items
    - add-integration: add BlockMeta to block structure template, add rules clarifying
      that tags must NOT appear on BlockConfig and integrationType must be a valid enum
      value; update registry snippet to include blocksMeta; add checklist items
    
    * fix(integrations): fix category dropdown by defining missing LANDING_INTEGRATIONS_DATA_PATH and regenerating integrations.json
    
    The staging merge introduced landing-content.ts but forgot to define
    LANDING_INTEGRATIONS_DATA_PATH in generate-docs.ts, causing the script
    to crash before writing integrations.json.
    
    The stale JSON had integrationTypes (plural array) from an older script
    version, while the Integration type and workspace UI both read
    integrationType (singular string) — so ALL_CATEGORY_SECTIONS bucketed
    to undefined and the category filters never appeared in the dropdown.
    
    Fixed by adding the missing path constant and re-running the generator.
    integrations.json now has 192 entries with the correct integrationType field.
    
    * fix(sidebar): restore resize handle on all pages
    
    commit 3109104582 wrapped the resize handle in {(isCollapsed ||
    isOnWorkflowPage) && ...} and added a useEffect that resets sidebar
    width to SIDEBAR_WIDTH.MIN whenever the user navigates away from a
    workflow page. Together these made the sidebar non-resizable on Tasks,
    Tables, Knowledge Base, and every other non-workflow page.
    
    Restore the staging behavior: always render the resize handle and
    remove the effect that forced the width reset on page transitions.
    
    * fix(sidebar): match staging onKeyDown and tabIndex on resize handle
    
    The resize handle was still conditionalizing onKeyDown and tabIndex
    on isCollapsed, blocking keyboard accessibility of the separator role
    when expanded. Staging always attaches both unconditionally.
    
    onKeyDown={isCollapsed ? handleEdgeKeyDown : undefined} → onKeyDown={handleEdgeKeyDown}
    tabIndex={isCollapsed ? 0 : undefined}                 → tabIndex={0}
    
    * feat(integrations): show connected credentials on integration detail page
    
    When navigating to /integrations/google-docs (or any integration), a
    Connected section now appears above the templates listing all workspace
    credentials tied to that provider. Each row links back to the credential
    detail page (/integrations/connected/${id}) for management actions.
    
    Pairs with the earlier change that routes connected items from the
    integrations list to the provider detail page instead of directly to
    the credential detail page.
    
    * fix(integrations): rename Add in chat to Add to Sim
    
    * fix(skills): rename Add button to Add to Sim
    
    * fix(platform): restore M1/M2/M3 regressions and LazyMotion on landing page
    
    M1 — Invitation guard: re-introduce usePermissionConfig().isInvitationsDisabled
    alongside the workspace inviteDisabledReason check. The flag now also
    respects NEXT_PUBLIC_DISABLE_INVITATIONS and EE permission-group
    disableInvitations, not just billing policy.
    
    M2 — Settings redirects: /settings/integrations and /settings/skills
    now server-redirect to /integrations and /skills respectively so old
    bookmarks and emails don't silently land on General.
    
    M3 — Starter block search exclusion: restore block.type !== 'starter'
    guard in the search store so users cannot add a duplicate Starter block
    via the command palette.
    
    LazyMotion: restore LazyMotion + domMax/domAnimation wrappers and m.*
    components in landing-preview-panel and landing-preview-home. The
    removal was accidental (the full motion bundle was left after an
    import cleanup), which caused the entire framer-motion feature set to
    load eagerly on the landing page.
    
    * fix(integrations): revert connected list to credential detail; remove settings redirects
    
    * feat(sidebar): restore workspace switcher search with updated styling
    
    Shows a search input in the workspace dropdown when the user has more
    than 3 workspaces (WORKSPACE_SEARCH_THRESHOLD). Keyboard navigation:
    ArrowDown/Up to move through results, Enter to switch, resets on close.
    
    Styled to match the current branch (border-1/surface-5 tokens, sm text,
    11px Search icon) rather than the old staging styles. Highlight state
    is wired through chipVariants active prop so it follows the same active
    appearance as clicked/hovered items.
    
    * fix(sidebar): clean up workspace search — layout, memo, and effect guard
    
    * fix(sidebar): align workspace rename input selection style with workflow rename
    
    * perf(sidebar): eliminate React re-renders during sidebar drag resize
    
    Previously, every mousemove during resize called setSidebarWidth(), which
    both updated the --sidebar-width CSS variable (sync) and set Zustand state
    (async). This caused:
      1. A 1-frame transition flash on mousedown — isResizing state had to
         round-trip through React before the is-resizing CSS class was applied,
         so the width transition fired for the first pixel of movement.
      2. A React re-render per pixel dragged — components reading sidebarWidth
         from the store (avatars, usage-indicator) lagged one frame behind the
         container, making the + and ... buttons appear to jump ahead.
    
    New approach:
      - handleMouseDown adds is-resizing directly to the sidebar DOM node before
        any React involvement (synchronous, no frame lag).
      - mousemove writes only to the CSS custom property (zero React renders).
      - mouseup persists the final width to Zustand exactly once.
      - isResizing / setIsResizing state removed from the store and hook — they
        are no longer needed since the class is managed via direct DOM mutation.
    
    * perf(sidebar): add requestAnimationFrame throttle to resize mousemove handler
    
    * fix(sidebar): fix drag-right lag caused by WorkspaceChrome overflow-hidden transition
    
    The sidebar-container's is-resizing class correctly suppressed its own width
    transition, but the two wrapper divs in WorkspaceChrome both have
    transition-[width]/transition-transform with a 175ms ease. The outer wrapper
    also has overflow-hidden, so while the sidebar content was at the correct width
    instantly, it was visually clipped by the outer wrapper which was still
    animating — causing the + and ... buttons to appear to lag behind the resize
    line on drag-right (not on drag-left, since shrinking doesn't clip content).
    
    Fix: add sidebar-shell-outer/sidebar-shell-inner class names to both chrome
    wrappers, and suppress their transitions via html.sidebar-resizing rule when
    a drag is active. The html.sidebar-resizing class is toggled directly in the
    resize hook alongside is-resizing, so it takes effect synchronously on mousedown.
    
    * fix(icons): redesign Download icon to match Upload style; fix Upload/Download confusion
    
    Download icon was missing the tray/shelf line at the bottom that Upload has,
    making it look like a plain arrow rather than a matched pair. Updated Download
    to use the same viewBox, stroke weight, and three-path structure as Upload
    (tray + stem + arrowhead), just pointing down.
    
    Also fix 5 places where Upload (↑) was incorrectly used for download/export
    actions:
      - files.tsx: two Download action rows in toolbar and context menu
      - tables/table.tsx: Export CSV toolbar button
      - table-context-menu.tsx: Export CSV context menu item
      - logs.tsx: Export toolbar button
      - landing-preview-logs.tsx: decorative Export button
    
    Import CSV and actual upload actions correctly keep the Upload icon.
    
    * fix(icons): replace Upload with Download on all remaining export/download actions
    
    - panel.tsx: Export workflow dropdown item
    - context-menu.tsx: Export in sidebar workflow context menu
    - chat.tsx: Export chat button
    - output-panel.tsx: Export console CSV button
    - terminal.tsx: Export console CSV button
    - resource-content.tsx: Export table as CSV + Download file buttons
    
    * fix(icons): fix remaining Upload→Download on download actions in files and logs
    
    - action-bar.tsx: download button in files toolbar
    - file-row-context-menu.tsx: Download item in file context menu
    - file-download.tsx: both download buttons in log details file viewer
    
    * updated block skills, settings pages, modals, buttons -> chips, blocks missing metadata
    
    * updated skill modal
    
    * improvement(resource-header): refine breadcrumb truncation ux
    
    * improvement(resource): add floating overflow text tooltips
    
    * wire up credits counter
    
    * improvement(resource-header): mute path dropdown title
    
    * refactor(resource-header): share floating-tooltip engine, prune dead overlay tooltips (#4844)
    
    Clean up the breadcrumb truncation feature for reuse and correctness:
    
    - Extract useFloatingTooltip / useIsOverflowing / FloatingTooltip into a shared
      floating-tooltip module. BreadcrumbSegment and FloatingOverflowText now consume
      one implementation instead of duplicating ~150 lines of positioning, velocity,
      overflow-detection, and portal logic.
    - Replace the hardcoded terminal-label regex in ResourceHeader with a typed
      `terminal` flag on BreadcrumbItem (set by the document chunk/loading crumbs),
      decoupling the generic header from knowledge-base copy.
    - Clear the path-popover close timeout on unmount and reuse the shared
      POPOVER_ANIMATION_CLASSES constant.
    - Drop the redundant manual overflow-state writes (fixes a sticky fade mask).
    - Revert FloatingOverflowText inside Combobox `overlayContent` back to plain
      truncating spans across files/logs/tables/scheduled-tasks/document: the combobox
      overlay is pointer-events-none, so the tooltip handlers never fired there.
    
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    
    * refactor(emcn,resource-header): address PR #4844 review feedback
    
    - useIsOverflowing now uses a callback ref so the ResizeObserver follows the
      element across mount/unmount/reassignment instead of capturing it once at mount.
      Safe for conditionally rendered consumers of the shared hook. (greptile P2)
    - Move POPOVER_ANIMATION_CLASSES out of chip-date-picker implementation internals
      into emcn/components/popover/popover-animation.ts, exported from the
      @/components/emcn barrel. Consumers now import from the module boundary.
      (greptile P2)
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * upgrade table and styling upgrade
    
    * fix schema to include integration
    
    * fix(files): align delete icon with tables view (Trash → Trash2)
    
    Co-Authored-By: waleed <waleed@simstudio.ai>
    
    * fix(mothership): preserve blockType for integration contexts in sent messages
    
    Integration mention chips were missing their provider icons in sent messages
    because blockType was dropped when mapping ChatContext to messageContexts.
    renderIntegrationTile returns null without blockType, silently hiding the icon.
    
    * fix(mothership): allow 'integration' resource type in chat resources API
    
    The VALID_RESOURCE_TYPES allowlist was missing 'integration', causing a
    400 error when adding integrations to the Mothership resource tab — so
    they never persisted and disappeared on refresh.
    
    * fix(ui): Add "File" title next to file resource header
    
    * fix(ui): fix resource header columns being bolded
    
    * fix(resource): keep the floating tooltip from jumping on click
    
    Gate the focus-driven show behind :focus-visible so a mouse click (which
    focuses the trigger) no longer re-shows the tooltip anchored to the element's
    bottom edge. On click the tooltip now hides cleanly instead of jumping down;
    keyboard focus still shows it.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * perf(sidebar): eliminate unnecessary re-renders in workspace switcher for non-search users
    
    - onMouseEnter: only set highlightedIndex when showSearch is true, preventing
      a state update + re-render on every workspace row hover for users with ≤ 3
      workspaces where the search is never shown
    - onOpenChange: only reset workspaceSearch and highlightedIndex when showSearch
      is true, since both values are always already at their defaults for non-search
      users and setting them triggers a pointless re-render during dropdown close
    - data-workspace-row-idx: only set when showSearch is true since the scroll
      effect that reads this attribute is already gated on showSearch
    
    * feat(search): context-aware cmd-k results on the integrations page
    
    When cmd-k is opened on the integrations page, show two new result
    groups: connected accounts (visible even with empty input) and catalog
    integrations (appear once the user types). Selecting an OAuth integration
    deep-links to its detail page with ?connect=oauth so the connect modal
    auto-opens. Non-OAuth integrations navigate to the plain detail page.
    
    Both groups are gated to the integrations page only and respect the
    hideIntegrationsTab permission. The credentials fetch shares the same
    React Query cache key as the integrations page itself (no double fetch).
    
    * refactor(emcn): make the floating tooltip the one canonical Tooltip
    
    Replace the Radix-based emcn Tooltip with the cursor-following floating tooltip so
    every tooltip in the app uses one consistent style. Built on the shared
    floating-tooltip engine (relocated into emcn), not a parallel implementation.
    
    - Move the floating-tooltip engine into emcn/components/tooltip and export it from
      the barrel; re-point its consumers (FloatingOverflowText, resource-header)
    - Extend the FloatingTooltip bubble to render arbitrary children (+ role/id for
      a11y) so it can back general tooltips, not just overflow text
    - Rebuild emcn Tooltip (Root/Trigger/Content/Provider/Shortcut/Preview) on
      useFloatingTooltip — compound API preserved, ~350 call sites unchanged, legacy
      side/align props accepted and ignored (the tooltip follows the cursor). Removes
      @radix-ui/react-tooltip usage (package kept for a later cleanup; react-slot
      retained for asChild)
    
    Note: general tooltips now show instantly (no hover delay) and follow the cursor.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * style(emcn): put tooltip text on the design scale (text-caption)
    
    Replace the tooltip's ad-hoc `text-xs` + `leading-[18px]` with the semantic
    `text-caption` (12px) font-size token so the text styling is fully on the design
    scale and self-documenting, matching how the rest of the system is set up. The
    color already used the global `--text-body` token. No visual change (still 12px
    with a ~18px line height).
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * feat(sidebar): add empty task state and inline task creation
    
    - Show "No tasks yet" in the Tasks section (expanded and collapsed) when the list is empty
    - Clicking + now creates a task via the API and navigates directly to it, rather than navigating to home
    - Add isCreatingTaskRef guard to prevent double-click from spawning multiple tasks
    - Disable + button while creation is pending
    - Fall back to home navigation on creation error
    
    * invite, billing, home
    
    * improvement(seats): auto purchase seats on invitations into workspace (#4857)
    
    * improvement(seats): auto purchase seats on invitations into workspace
    
    * improve sampling for seat drift reconciler
    
    * address comments
    
    * feat(knowledge): align connector UI with integrations page styling
    
    - ConnectorTypeCard now matches integration rows: brand-colored rounded-xl tile, ArrowRight, title/subtitle hierarchy
    - ConnectorCard icon upgraded from flat surface-4 to branded tile (white icon on brand bg, graceful fallback)
    - Connector header badges use chipVariants instead of custom Button classes
    - Add-connector search input aligned to integrations style (h-[30px], rounded-lg, border-1)
    
    * fix(icons): trim Folder SVG viewBox to remove right-side whitespace
    
    The folder path only extends to x≈14.33 in a 15-unit viewBox, leaving
    ~0.5 units of empty space on the right. At 12px rendered size this
    produces ~0.4px extra gap (visible as ~1px on retina displays) compared
    to solid icons like the workflow color square. Trimming the viewBox to
    14.5 units makes the folder fill its chip slot evenly.
    
    * fix(user-input): restore draft text synchronously to preserve contexts on nav
    
    The SSR-safe approach (empty useState + effect restore) created a timing
    window where the sync effect in useContextManagement fired with message=''
    before the value was set, clearing any restored contexts. Folder and workflow
    contexts (not re-added by applyAutoMentions) were lost on every nav-back.
    
    Revert to the staging approach: initialize value synchronously from the
    draft store so message is already populated when effects run, matching
    the behavior on staging.
    
    * fix(queue): render context chips in queued messages
    
    - Remove plainMentions from queued message rows so context chips render
      with icons, consistent with sent messages
    - Fix computeMentionRanges to use '/' prefix for skill contexts (content
      has the slash trigger restored at submit time, not '@')
    
    * fix(mothership): remove integrations from add-resource dropdown
    
    * fix(mothership): comment out integrations from add-resource dropdown
    
    * chore(db): drop form, templates, template_creators, template_stars tables
    
    These tables backed the Forms and Templates platform features which were
    intentionally removed from this branch. Clean up the DB schema to match.
    
    * chore(db): add migration metadata for 0224 drop tables
    
    * block icons, sidebar, toolbar
    
    * chore: remove remaining dead code for template-profile feature
    
    - Remove 'template-profile' from SettingsSection union type
    - Remove 'template-profile' entry from SECTION_TITLES
    - Remove now-redundant template-profile guard in settings sidebar
    - Remove commented-out template-profile nav item
    
    * fix(multi-select): preserve anchor on range selection for tasks and folders
    
    After a shift+click range, the anchor (lastSelectedTaskId / lastSelectedFolderId)
    was being updated to the end of the range (toId). This caused subsequent
    shift+clicks to extend from the wrong point instead of the original click.
    
    Standard behavior: anchor stays at the initial click (fromId) so repeated
    shift+clicks always expand/contract relative to where you started.
    
    * feat(emcn): add SearchInput component and unify search bars platform-wide
    
    - Add SearchInput to emcn: 30px chip-family filled search input matching the
      integrations page pattern (border-1, surface-5, leading Search icon)
    - Migrate all 22 search bars across settings, EE pages, and integrations to
      SearchInput (only layout classes allowed at callsites)
    - Rename Sim Keys -> Sim API Keys in nav/title; page copy now says API key
    - Remove components/ui input, label, and verified-badge; migrate consumers
      to emcn equivalents or raw inputs (table cell editor, wand prompt bar)
    - Delete dead EE skeleton files (data-drains, data-retention)
    - General settings: Home Page chip moves to header left as navigation
    
    * fix(files,tables): restore new-file editor autofocus and CSV import error toasts
    
    Both were dropped on the staging line and regressed vs production (main):
    
    - Files: the new-file editor autofocus chain (files.tsx -> file-viewer ->
      text-editor) was stripped by the react-doctor dead-code pass in #4544,
      which misread the prop-drilled `autoFocus` (consumed by an imperative
      `editor.focus()` effect) as unused. Restored the prop through all three
      layers and the one-shot focus effect so creating a new file focuses the
      editor immediately.
    - Tables: CSV import failures were silently logged with no user feedback.
      Restored the per-file and generic `toast.error` surfacing.
    
    * feat(home): score suggested actions by workspace signals
    
    - Derive the suggestion pool from the curated block template catalog
      (1,343 prompts across 172 blocks) instead of 15 hardcoded entries
    - Fix inverted relevance: prompts for connected providers are now boosted
      4x (instantly runnable) instead of excluded; unconnected discounted 0.4x
    - Weight by featured (3x), popular category (1.5x), and resource gaps
      (no tables -> boost table starters; has KBs -> dampen KB-creation prompts)
    - Weighted sampling without replacement, max one suggestion per block
    - Connect rows weighted by catalog template count; 2 for fresh workspaces,
      1 once something is connected
    - Key the catalog map by both versioned and base block types so gmail_v2
      templates resolve (gmail, github, notion, linear were silently dropped)
    - Replace derive-in-effect state with a useMemo keyed by a shuffle nonce
    - Add suggested_action_clicked / suggested_actions_shuffled /
      suggested_actions_toggled PostHog events
    
    * billing, teammates
    
    * improvement(credentials): credentials invites, secrets tab wiring up (#4874)
    
    * improvement(credentials): move away from invite notion
    
    * wire up secrets ui/ux
    
    * address comments
    
    * get consistent styling by removing emcninput + text area
    
    * styling consistency
    
    * remove fallback
    
    * address comment:
    
    * refactor(ui): migrate settings & workspace UI to chip design system
    
    Migrate modals to ChipModal (showDivider, hint, resizable, size, leading,
    ChipModalTabs), standardize Chip variants, add ChipCombobox wrapper, and apply
    chip inputs/dropdowns across settings, knowledge, logs, tables, inbox, EE tabs.
    Render ChipModalTabs as a ChipSwitch segmented control. Align /settings/secrets
    detail with /integrations, refresh whitelabeling, and restore file-editor
    autofocus and CSV import error toasts.
    
    * fix(mothership): restore integrations to useAvailableResources for @ mention
    
    Integrations were fully removed from useAvailableResources which broke
    the @ mention menu since user-input shares the same hook. Now integrations
    are always included in the hook but excluded at the AddResourceDropdown
    component level, keeping them out of the sidebar + menu while remaining
    available for @ mention autocomplete.
    
    * refactor(settings): chip design-system consistency pass across all tabs
    
    Extract a shared chip-field shell (CHIP_FIELD_SHELL/CHIP_FIELD_INPUT) mirroring
    Input variant='chip' and route secrets, credential detail, and integrations
    credential detail through it (30px height, font-medium, focus ring). Add a
    Discard action to the secrets header when dirty. Group BYOK providers into
    Models/Search & web/Enrichment sections and align its tiles to the integrations
    tile.
    
    Normalize list-row typography to text-[14px]/text-[12px] and icon tiles to
    rounded-xl + border across api-keys, copilot, custom-tools, mcp,
    workflow-mcp-servers, credential-sets, and access-control. Tone the secrets
    Details chip and per-row affordances to ghost. Fix token correctness: raw
    tailwind colors to design tokens (data-drains), missing chip variant on the
    Snowflake role input, ColorInput chip-field reuse (whitelabeling), error token
    and icon sizes (workflow-mcp-servers), border token (mcp), no-results sizing
    (secrets), row chrome (recently-deleted), hover token and Button to Chip
    (access-control), and deduped textarea chrome (sso).
    
    * feat(settings): unify filter dropdowns on ChipSelect (integrations style)
    
    Add a ChipSelect emcn component — a filled chip trigger + chevron opening a
    DropdownMenu, matching the integrations category filter — supporting single
    select, multi-select (checkbox rows), grouped options, and optional in-menu
    search. Migrate every settings/EE filter dropdown off ChipCombobox to it:
    audit-logs (resource-type multi + time-range), data-retention, data-drains,
    general, admin (grouped tool picker), inbox status filter, and the workflow
    MCP-server pickers. The SSO provider-id field stays an editable combobox since
    it accepts free-text slugs.
    
    Also fix audit findings: chip the MCP client-secret input, normalize an MCP
    error-text size, and drop now-dead destination-icon code in data-drains.
    
    * fix(mentions): require explicit @ for integration mentions; decorate sent messages robustly
    
    - Bare integration names in prose (Monday, Notion, Clay) are no longer
      auto-converted to mentions or chipped — mention treatment is strictly
      opt-in via a token-starting @ (fixes the scunthorpe problem)
    - @-prefixed mentions still canonicalize casing (@slack -> @Slack) on both
      the keystroke fast-path and bulk paths (paste, template, draft, STT)
    - Sent/queued messages now self-sufficiently decorate @IntegrationName
      tokens via a text scan, covering messages sent before the input pass
      ran or authored outside the chat input
    - Integration contexts missing a resolvable blockType (messages persisted
      before blockType was saved) are backfilled by label lookup so their
      mention pills render the brand icon again
    
    * refactor(settings): section the API keys page like secrets
    
    Wrap Workspace, Personal, and the allow-personal-keys toggle in SettingsSection
    (muted label + divider) instead of bare bold headers, matching the secrets and
    BYOK pages.
    
    * fix(settings): ChipSelect renders above modals + full-width form mode
    
    Raise the ChipSelect menu to --z-popover so it layers above modal surfaces
    (--z-modal) instead of opening behind them. Add a fullWidth prop that stretches
    the trigger and right-aligns the chevron for form-field use, and apply it to the
    workflow MCP-server pickers.
    
    * fix(emcn): ChipSelect uses the emcn flat chevron, not lucide's square one
    
    The lucide ChevronDown is square; rendering it at the chip's 9x7 footprint
    stretched it. Switch to the custom emcn ChevronDown (built for that wide aspect),
    matching the integrations filter and ChipDropdown.
    
    * fix(emcn): ChipSelect trigger hugs its content (w-fit)
    
    In a stacked form layout the trigger was stretched by align-items: stretch,
    leaving an empty gap to the right of the value. Add w-fit so the chip sizes to
    its content (a compact pill) everywhere; fullWidth form selects are unaffected.
    
    * fix(emcn): ChipSelect uses a square lucide chevron
    
    Revert to lucide's ChevronDown sized square (size-[14px]) so it renders crisp,
    matching the standard select chevron used by Combobox.
    
    * renamed tasks to chats
    
    * rename and file change
    
    * improvement(billing): wire up billing, org, teammates tabs + remove deprecated subscription tab (#4887)
    
    * improvement(billing): wire up billing, org, teammates tabs + remove depr subscription tab
    
    * pass exec timeout to tool routes
    
    * reuse helper
    
    * address comments
    
    * address disable comment
    
    * chore(db): remove migration 0224 to regenerate on top of staging
    
    Co-authored-by: Cursor <cursoragent@cursor.com>
    
    * fix type errors and regen migration?
    
    * chore(db): drop branch migration 0226 ahead of staging merge; will regenerate
    
    * chore(db): regenerate migration 0226 after staging merge
    
    * externalize before compaction in fallback'
    
    * fix save/discard chips to be consistent
    
    * fix(ui): remove smodal tabs in favor of chip modal tabs
    
    * fix(ui): skip auto-scrolling on mouse highlight of workspace
    
    * fix(platform): restore settings redirects, forgot-password Enter submit, and tag tooltip visibility
    
    - Re-add SETTINGS_REDIRECTS so /settings/integrations and /settings/skills
      deep links redirect to their top-level routes instead of rendering an
      empty settings panel (accidentally removed in 86da193cc3 one minute
      after cca5054cf6 added it)
    - Add opt-in onSubmit to ChipModalField input/email variants and wire it
      in the forgot-password modal so Enter submits again (lost in the
      ChipModal conversion)
    - Knowledge tag tooltip: drop the max-h/overflow-y-auto clamp that the
      pointer-events-none floating tooltip made unreachable; truncate each
      tag row instead so all tags stay visible with bounded height
    
    * chore(db): remove migration 0226_third_spot before staging merge
    
    * chore(db): regenerate migration as 0227 after staging merge
    
    * feat(telemetry): add posthog + audit coverage for new platform actions
    
    Audit log (compliance/permission-relevant only):
    - org_seat.provisioned — seat auto-purchased when an invite acceptance
      grows the org (actor = accepting user, includes seat delta)
    - org_plan.converted — Pro→Team conversion triggered by invite acceptance
    - org_seat.drift_reconciled — hourly cron …
    10 people authored Jun 6, 2026
    Configuration menu
    Copy the full SHA
    0075ab9 View commit details
    Browse the repository at this point in the history
  2. 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).
    waleedlatif1 authored Jun 6, 2026
    Configuration menu
    Copy the full SHA
    20a00a1 View commit details
    Browse the repository at this point in the history

Commits on Jun 7, 2026

  1. 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
    TheodoreSpeaks authored Jun 7, 2026
    Configuration menu
    Copy the full SHA
    15df511 View commit details
    Browse the repository at this point in the history
  2. feat(tables): row-gutter drag-select, Cmd+F find, and select-all poli…

    …sh (#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>
    TheodoreSpeaks and claude authored Jun 7, 2026
    Configuration menu
    Copy the full SHA
    c90a1eb View commit details
    Browse the repository at this point in the history

Commits on Jun 8, 2026

  1. improvement(perms): member removal reassignment policies (#4906)

    * improvement(perms): member removal reassignment policies
    
    * improve UI notif
    
    * address comments
    icecrasher321 authored Jun 8, 2026
    Configuration menu
    Copy the full SHA
    2c7b1ca View commit details
    Browse the repository at this point in the history
  2. 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.
    waleedlatif1 authored Jun 8, 2026
    Configuration menu
    Copy the full SHA
    d526b23 View commit details
    Browse the repository at this point in the history
  3. 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
    TheodoreSpeaks authored Jun 8, 2026
    Configuration menu
    Copy the full SHA
    ec256d2 View commit details
    Browse the repository at this point in the history
  4. feat(enrichment): add ZeroBounce, NeverBounce, and MillionVerifier em…

    …ail 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>
    3 people authored Jun 8, 2026
    Configuration menu
    Copy the full SHA
    2577135 View commit details
    Browse the repository at this point in the history
  5. fix(user-input): atomic chip selection, modifier-key handling, and st…

    …ale 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)
    waleedlatif1 authored Jun 8, 2026
    Configuration menu
    Copy the full SHA
    27fc6dd View commit details
    Browse the repository at this point in the history
  6. feat(integrations): suggest curated skills per integration with one-c…

    …lick 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.
    waleedlatif1 authored Jun 8, 2026
    Configuration menu
    Copy the full SHA
    e257d06 View commit details
    Browse the repository at this point in the history

Commits on Jun 9, 2026

  1. 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
    waleedlatif1 authored Jun 9, 2026
    Configuration menu
    Copy the full SHA
    ffd87a3 View commit details
    Browse the repository at this point in the history
  2. 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.
    TheodoreSpeaks authored Jun 9, 2026
    Configuration menu
    Copy the full SHA
    7677479 View commit details
    Browse the repository at this point in the history
  3. improvement(metrics): emit hosted-key metrics to CloudWatch instead o…

    …f 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
    TheodoreSpeaks authored Jun 9, 2026
    Configuration menu
    Copy the full SHA
    7434df9 View commit details
    Browse the repository at this point in the history
  4. 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>
    3 people authored Jun 9, 2026
    Configuration menu
    Copy the full SHA
    4f00baf View commit details
    Browse the repository at this point in the history
  5. 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)
    waleedlatif1 authored Jun 9, 2026
    Configuration menu
    Copy the full SHA
    a72e35e View commit details
    Browse the repository at this point in the history
  6. feat(emcn/toast): toast redesign — intent variants, stacking, hover r…

    …eveal, 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>
    4 people authored Jun 9, 2026
    Configuration menu
    Copy the full SHA
    05408fd View commit details
    Browse the repository at this point in the history
  7. fix(tables): stop insert-row flicker and return order_key from rows l…

    …ist (#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)
    TheodoreSpeaks authored Jun 9, 2026
    Configuration menu
    Copy the full SHA
    efeacb9 View commit details
    Browse the repository at this point in the history
  8. 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)
    TheodoreSpeaks authored Jun 9, 2026
    Configuration menu
    Copy the full SHA
    f7811f8 View commit details
    Browse the repository at this point in the history
  9. feat(models): add Claude Fable 5 (#4921)

    * feat(models): add Claude Fable 5
    
    * docs(anthropic): note Fable 5 in buildThinkingConfig docblock
    waleedlatif1 authored Jun 9, 2026
    Configuration menu
    Copy the full SHA
    b12b0f1 View commit details
    Browse the repository at this point in the history
Loading