Commit Graph
3969 Commits
Author SHA1 Message Date
Vikhyath Mondreti b2d9c02bb9 fix(chat): hosted key execution via call-integration-tool (#5745)
* fix(chat): hosted key execution via call-integration-tool

* fix optional hosted key
2026-07-17 14:04:35 -07:00
Waleed ec75084dbf fix(billing): standardize credit-usage summary font to proportional figures (#5744) 2026-07-17 13:58:35 -07:00
WaleedandMarcus Chandra fb86b499f8 feat(gitlab): dynamic access levels + group & membership ops + full group filters (#5743)
* feat(gitlab): dynamic access levels + group and membership operations

Access levels can now be bound to runtime references (e.g. a policy-table
lookup), not just picked from a static list. The three access-level fields
(accessLevel, memberAccessLevel, invitationAccessLevel) switch from dropdown
to combobox so a reference expression is accepted at Copilot save-time instead
of being rejected as an invalid enum value. The resolved value is validated at
execution time by coerceGitLabAccessLevel, which accepts an integer, a numeric
string, or a level name ("Developer"), and throws a clear error otherwise -
preserving the real safety property (no bad integer reaches GitLab) while
dropping the false one (levels must be known at design time).

Also closes demand gaps from the AskIT data:
- get_group / list_groups: resolve and list groups (provisioning critical path)
- list_user_memberships: admin GET /users/:id/memberships, gated in its
  description; the non-admin path is composing list_members

The access-level enum is now a single source of truth in tools/gitlab/utils.ts
(GITLAB_ACCESS_LEVELS) that the block options and runtime coercion both derive
from, replacing three inline copies.

* fix(gitlab): treat access level 0 ("No access") as a provided value

A runtime reference can resolve to the integer 0, but the block still gated
access-level presence with truthiness: required ops rejected 0 as missing and
optional ops silently omitted it. Add hasGitLabAccessLevel(value) (0 and '0'
are provided; undefined/null/'' are not) and use it for all six presence
gates so "No access" can be granted or set via a reference.

* feat(gitlab): expand group listing filters and harden access-level enum

- list_groups: add visibility, min_access_level, and all_available filters (documented on GET /groups) plus order_by/sort, now surfaced in the block and forwarded in params
- order_by widened to include GitLab's documented 'similarity' value
- access-level enum label 'Minimal Access' -> 'Minimal access' to match GitLab verbatim; coercion already case-insensitive
- min_access_level guard rejects 0 (GitLab floor is 5); reuse the access-level enum for the block dropdown (drops 'No access')
- tests: out-of-enum numeric-string coercion case + full list_groups filter mapping

* fix(gitlab): validate list_groups min-access-level and similarity ordering at execution time

Greptile round 1 (both P1):
- min_access_level: coerce via the shared access-level enum (coerceGitLabMinAccessLevel) and throw on out-of-enum values (31, 999) or 0, so a direct tool call fails loudly instead of sending an invalid filter to GitLab
- order_by=similarity now requires a non-empty search term (GitLab ignores similarity ordering without one); throws a clear error otherwise
- tests for both validations

---------

Co-authored-by: Marcus Chandra <mzxchandra@gmail.com>
2026-07-17 13:40:23 -07:00
Siddharth Ganesan 772b710e39 fix(mothership): canonical folder ids (#5741)
* folder fix

* fix bug
2026-07-17 13:13:15 -07:00
Waleed 92feb24cb9 perf(workflow): scope resize CSS-var writes to the container, not :root (#5738)
* perf(workflow): scope resize CSS-var writes to the container, not :root

Resizing the editor panel, terminal, output panel, and sidebar was still
laggy on large workflows even after the drag handles stopped re-rendering
React per frame. A CDP trace showed the cost was style recalculation, not
JS or layout: ~3.9s of Document::recalcStyle over a 50-move panel drag.

Root cause: each drag frame wrote its CSS variable to document.documentElement.
On a large document (~42k elements) any inline custom-property write on the
<html> root recalculates style for the whole tree — measured at ~77ms per
write, independent of what actually reads the variable (an unused var costs
the same). Writing the same variable to the element that consumes it recalcs
only that subtree (~0.5ms) — a ~150x reduction.

useDragResize now writes the variable to a caller-provided target element
during the drag and reconciles to :root once on release (so on-demand readers
and the pre-hydration script are unchanged): panel -> .panel-container,
terminal -> .terminal-container, output panel -> .terminal-container (both
consumers inherit it), sidebar -> .sidebar-shell-outer. The terminal's
expanded-threshold sync writes store state directly instead of setTerminalHeight
so it no longer touches :root mid-drag.

Measured (near-empty canvas, 50-move drag): panel style+layout 3891ms -> 56ms,
frame p95 91.6ms -> 8.4ms, main-thread blocking 4566ms -> 0ms; terminal
recalc 3899ms -> 16ms, blocking 5558ms -> 0ms; sidebar recalc ~77ms/frame -> ~1ms/frame.

* perf(workflow): keep float boundary-sync live during scoped resize drags

The resize hooks now write their CSS variable to the consuming container
element during a drag (not :root), so use-float-boundary-sync's MutationObserver
on :root no longer fired mid-drag and open floats (chat, search-replace,
variables) only re-clamped on release. Read each boundary dimension from its
scoped element with a :root fallback, and observe the container elements as
well as :root, so an open float tracks the drag live exactly as before.

* fix(workflow): finalize drag on unmount + clamp reads scoped output width

- useDragResize: unmounting mid-drag now runs endDrag (commit + drop the
  scoped override) instead of a bare cleanup, so navigating away can neither
  lose the resize nor strand an inline override on a surviving target element.
- terminal output-panel clamp: read the live --output-panel-width from the
  terminal element first (where a drag writes its scoped override), then :root,
  then the store, so a mid-drag terminal/window resize can't clamp against a
  stale value.

* fix(workflow): robust drag finalize — last-applied value, sidebar unmount, clamp skip

Addresses three review findings on the scoped-resize change:
- useDragResize: track the last applied value and commit that on release;
  only recompute from the pointer event while the target is still connected.
  On an unmount the target's rect can be detached, so a layout-reading compute
  (e.g. the output panel's) would return a degenerate MIN — committing the last
  shown value avoids persisting the wrong width, and keeps flick-safety on a
  normal release.
- use-sidebar-resize: finalize on unmount (run endDrag, not bare cleanup) so a
  drag interrupted by unmount persists the width and drops the scoped override.
  This matters more than for the panel/terminal because .sidebar-shell-outer
  lives in the workspace chrome and outlives the sidebar, so a stranded override
  would win over :root.
- terminal output-panel clamp: skip while an output-panel drag is active (its
  scoped inline override is present). That drag's own compute clamps every frame
  against the live terminal rect, and a store-driven :root write here would be
  masked by the inline override anyway.

* fix(workflow): sidebar flick-safety + clamp reads committed :root width

- use-sidebar-resize: compute the clamped width synchronously on each pointer
  move (storing lastWidth) and defer only the DOM write to rAF, so a fast flick
  released before the frame runs still commits the final pointer position
  instead of a stale frame or nothing.
- terminal output-panel clamp: when not mid-drag, read the committed
  --output-panel-width from :root (written synchronously by the store setter)
  rather than the React store value, which lags a render behind the commit;
  fall back to the store before any commit. Comment corrected to match.
2026-07-17 13:06:11 -07:00
Vikhyath Mondreti 345369266d fix(mothership): billing for mcps, hosted key tools (#5740)
* fix(mothership): attribution for direct exec tools

* fix hosted key charges for direct tool exec

* fix llm chat tool
2026-07-17 12:56:03 -07:00
Waleed 5eafa86992 feat(email): native Gmail API mail provider for GCP self-hosting (#5736)
* feat(email): native Gmail API mail provider for GCP self-hosting

Adds Gmail as a fifth transactional mail provider (Resend → SES → SMTP →
ACS → Gmail). GCP has no first-party SES/ACS equivalent, so the native
Google path is the Gmail API: a service account with domain-wide
delegation impersonates a Workspace sender (GMAIL_SENDER) and posts the
raw RFC 822 message (built via nodemailer's MailComposer — full parity
incl. attachments, replyTo, unsubscribe headers) to the media-upload
messages.send endpoint. The Workspace SMTP relay alternative is
documented against the existing SMTP provider.

* fix(email): review round 1 + audit hardening for Gmail provider

- a 2xx from Gmail with an empty/malformed body no longer surfaces as a send
  failure (the mailer's fallback chain would deliver the same email twice);
  covered by a regression test
- normalize bare-LF line endings in html/text bodies to CRLF (RFC 822) before
  composing the raw message
- add multi-recipient and text-only test cases
2026-07-17 12:55:21 -07:00
Theodore Li c9399f8413 fix(oauth): serialize version-guard date in slack fan-out sql (#5737) 2026-07-17 12:26:17 -07:00
Theodore Li 61de0b5808 feat(pii): custom user-supplied regex patterns for redaction (#5732)
* feat(pii): custom user-supplied regex patterns for redaction

* fix(pii): enforce custom-regex syntax + safety at the boundary schema

* improvement(pii): always wrap custom-pattern redaction token in angle brackets

* chore(pii): register guardrails_validate in the dev minimal tool registry

* fix(pii): coerce empty guardrails entity-type checkbox (null) so the contract accepts it

* fix(pii): keep detect-all when a custom pattern is added; custom patterns win overlaps
2026-07-17 15:20:58 -04:00
Siddharth Ganesan caa454aeca fix(mothership): bug fixes (#5735)
* fix(mothership): credential connect names, highlighted line, knowledge of connection

* fix(mothership): webhook url is now visible to the mothership

* fix(mship): tool name audit

* fix(mship): hosted key data

* fix(mship): show icons on question exed out

* fix(mship): redis replay

* fix(mothership): description and incremental vfs both nuked

* fix(ci): fix build

* fix(mship): tool names

* fix(mship): stream handling
2026-07-17 11:15:58 -07:00
Theodore Li f1deb31359 fix(oauth): coalesce slack token refresh per installation and preserve provider refresh errors (#5723)
* fix(oauth): coalesce slack token refresh per installation and preserve provider refresh errors

* fix(oauth): keep transient refresh failures errorless and size slack lock budgets past the provider timeout

* fix(oauth): let slack refresh followers poll for the lock's full lifetime

* chore(oauth): drop provider refresh-error surfacing to keep this PR slack-only

* fix(oauth): version-guard slack chain writes against concurrent connects

* fix(oauth): clear slack dead flag before connect fan-out
2026-07-17 13:30:49 -04:00
Waleed 7e6aeb2b7a feat(chat): favicon external links with secure link-preview tooltips (#5734)
* feat(chat): favicon external links with secure link-preview tooltips

* fix(link-preview): address review findings

- allow http fetches to match advertised http(s) link support
- fix meta content regex to handle apostrophes and either quote delimiter
- hash Redis cache keys so sensitive URLs are not stored verbatim
- add per-user rate limit to the outbound-fetching route
- render siteName-only previews instead of falling back to the URL

* fix(link-preview): redact full URLs from failure logs

* improvement(link-preview): render-time preview fetch, cheerio parsing, cleanup pass

- fetch previews when links render (emcn tooltip shows instantly — hover prefetch had no delay to race); tooltip reads the warmed cache, eliminating the URL-then-preview flash
- parse OG metadata with cheerio (already used server-side) instead of hand-rolled regexes + entity decoding, fixing double-decode and quote-handling classes
- drop the no-longer-needed prefetch hook; remove dead side prop on Tooltip.Content
- extract ExternalLink to a sibling module per component-size guidelines; fix TSDoc placement

* fix(link-preview): https-only previews and full-document parsing

- drop allowHttp: plain-http fetches would reach the URL validator's self-host loopback exception; previews are now explicitly https-only on both server (early null) and client (query never fires for http)
- parse the full capped document instead of truncating at the first <body> substring, which could match inside head scripts/comments and drop metadata

* improvement(chat): render mailto links as plain text
2026-07-17 10:10:30 -07:00
Waleed a19fce1cd8 improvement(workflow): zero-render drag-resize for panel, terminal, and output panel (#5730)
* improvement(workflow): zero-render drag-resize for panel, terminal, and output panel

Port the sidebar's pointer-capture + rAF + CSS-variable drag pattern to
use-panel-resize, use-terminal-resize, and use-output-panel-resize so a
drag writes only --panel-width/--terminal-height/--output-panel-width
per frame and commits to Zustand (one re-render + one localStorage
write) on pointerup. Previously every mousemove dispatched a store set,
re-rendering the whole always-mounted Panel tree (Chat/Editor/Toolbar)
and the terminal, plus a persist localStorage write per move. Also drop
the Panel's unused panelWidth subscription and drive the output panel
width via a CSS variable instead of React state.

* improvement(workflow): shared useDragResize hook + review fixes

Extract the drag mechanism into a shared useDragResize hook (pointer
capture, rAF-aligned apply, commit-on-release) consumed by the panel,
terminal, and output-panel resize hooks. Fixes from adversarial review:
commit the last computed value instead of reading the CSS var back (a
fast single-frame flick could be lost to a cancelled rAF, and a
pre-rehydration read returned '' -> NaN), floor the panel/terminal max
clamp at the minimum so narrow viewports can't invert the clamp, guard
pointerup/pointercancel by pointerId so a second touch pointer can't
kill the drag, and capture the terminal rect once on drag start instead
of per-frame getBoundingClientRect. Remove the now-dead isResizing store
state and centralize CONTENT_WINDOW_GAP in stores/constants.

* fix(workflow): compute drag value rAF-aligned from the latest pointer event

Run compute inside the rAF (before the CSS-var write, so any layout read
hits clean layout at most once per frame) and derive the final value from
the latest pointer event on release. The output-panel hook now captures
the terminal element on drag start and re-reads its rect per frame, so
the clamp stays correct when the terminal resizes mid-drag and the live
width can never exceed the current max.

* fix(terminal): clamp output panel against the live CSS-var width

The ResizeObserver clamp compared the persisted store width, which is
intentionally stale during a drag; a terminal shrink mid-drag could
overwrite the live width with a stale store value. Compare against the
live --output-panel-width variable (store as pre-write fallback) so the
clamp converges with the drag instead of fighting it.
2026-07-17 08:05:47 -07:00
Waleed 93ba3c48c5 feat(landing): X pixel conversion tracking on landing pages (#5731)
* feat(landing): X pixel conversion tracking on landing pages

* fix(landing): dedupe X pixel initial PageView by URL to survive Strict Mode effect replay

* fix(landing): scope X pixel URL dedupe to the tracker instance so same-URL returns to landing still track
2026-07-17 00:31:33 -07:00
Waleed 42b9d5f81d improvement(workflows): stop writing placeholder workflow descriptions (#5729)
* improvement(workflows): stop writing placeholder workflow descriptions

* fix(workflows): scrub placeholder descriptions at the import boundary

* test(workflows): pin import-boundary description scrubbing behavior
2026-07-17 00:25:04 -07:00
Waleed f6bb8e6d3e feat(storage): native Google Cloud Storage support for self-hosting (#5728)
* feat(storage): native Google Cloud Storage support for self-hosting

Adds GCS as a third object-storage backend with full parity with S3 and
Azure Blob: uploads, streaming downloads, deletes, head, V4 signed URLs
(single + batch), and browser/server multipart uploads via the GCS XML
API. Selection precedence is Azure Blob > S3 > GCS > local disk.

- new provider client at lib/uploads/providers/gcs (cached singleton,
  ADC/Workload Identity or inline GCS_CREDENTIALS_JSON auth)
- per-context GCS_*_BUCKET_NAME config wired through getStorageConfig
- shared getServeStoragePrefix() replaces hardcoded blob/s3 serve paths
- docs (object-storage, environment-variables), .env.example, helm
  values.yaml + values-gcp.yaml storage section

* fix(storage): review round 1 — GCS per-context bucket fallback + ETag quote normalization

- getGcsConfig falls back to the general bucket for every context (GCS bucket
  names are globally unique, so the S3-style sim-execution-files literal default
  would point at an unowned bucket; empty per-context buckets previously made
  uploads and downloads disagree)
- completeGcsMultipartUpload restores quotes on ETags stripped by the shared
  browser upload client before building the completion XML
- docs/.env.example/helm updated for the fallback behavior

* fix(storage): review round 2 — route chat authz and execution-URL detection through getStorageConfig

- getChatStorageConfig delegates to getStorageConfig('chat') (identical for
  S3/Azure, picks up the GCS general-bucket fallback instead of reading the
  raw chat config and rejecting valid chat files)
- parse route resolves the execution bucket via getStorageConfig('execution')
  for all providers, so GCS execution files in the fallback bucket are still
  recognized as our own objects

* fix(storage): validation pass — gcs serve-prefix parity in key parsers + CORS doc fix

- extractStorageKey, extractFilename, and extractEmbeddedFileRef now strip the
  gcs/ serve prefix like s3/ and blob/, so direct-uploaded files on GCS parse,
  delete, download, and embed correctly (previously only the serve route knew
  the prefix)
- file-download storageProvider union includes 'gcs'
- completeGcsMultipartUpload defensively rejects a 200 response carrying an
  XML error document
- docs: CORS example lists concrete x-goog-meta-* header names (GCS matches
  responseHeader entries exactly; wildcards are only supported for origin)
2026-07-17 00:15:14 -07:00
Vikhyath Mondreti 86e6e1d26c feat(forking): excluded workflows (#5727)
* feat(forking): excluded workflows

* improve sync preview
2026-07-16 20:23:26 -07:00
Vikhyath Mondreti 442eabaf27 improvement(checkout): enforce team/enterprise-only org subscription, block double-covered personal checkouts (#5715)
* improvement(checkout): enforce team/enterprise-only org subscriptions, block double-covered personal checkouts, and drop renewal-triggered workspace detach

* close race with personal pro / org inclusions

* address comments

* fix(billing): compute org coverage independently of the personal-sub lookup and fail closed on unverifiable plan writes
2026-07-16 19:16:44 -07:00
Theodore Li 9d5ed38cd1 feat(table): per-plan table dispatch concurrency with env overrides (#5720)
* feat(table): per-plan table dispatch concurrency with env overrides

* fix(table): enforce shared concurrencyKey cap on database batchEnqueueAndWait

* refactor(table): thread dispatch concurrency via invocation instead of persisting it

* improvement(table): collapse dispatch concurrency env vars to FREE/PAID
2026-07-16 21:16:07 -04:00
Theodore Li 1da346b703 feat(triggers): service-account credentials in the hubspot trigger + token-SA name-collision 409 (#5693) 2026-07-16 19:11:57 -04:00
Waleed 5944c7c54d feat(library): add best AI agent platforms 2026 comparison article (#5722)
* feat(library): add best AI agent platforms 2026 comparison article

* fix(library): count all eleven compared platforms and add Dust/Lindy table rows
2026-07-16 15:56:27 -07:00
Waleed 01ffacc2ed improvement(chat): give wsres resource links the clickable chip treatment (#5719) 2026-07-16 15:49:34 -07:00
Waleed db85ae998f fix(chat): align resource mention icon spacing with mention chips (#5718) 2026-07-16 14:13:10 -07:00
Waleed a218ebe4ed fix(sidebar): align workspace header loading-state spacing with chip geometry (#5717) 2026-07-16 14:09:59 -07:00
Waleed cea1c8940a feat(providers): add Kimi (Moonshot AI) provider (#5716)
* feat(providers): add Kimi (Moonshot AI) provider

* fix(providers): preserve reasoning_content in kimi tool loop
2026-07-16 13:59:42 -07:00
8adeaab8a5 feat(gitlab): access, membership, and user-admin operations (#5710)
* feat(gitlab): add access, membership, and user-admin tools

Adds member, invitation, access-request, SAML group link, and user
administration tools to the GitLab integration. Resource-scoped ops work
against projects or groups; user-admin ops require an admin token. All tools
reuse the existing host/SSRF guard via getGitLabApiBase and add a shared
getGitLabResourcePath helper.

* feat(gitlab): wire access operations into the GitLab block

Adds the new operations to the block dropdown and tools access list, with a
named access-level dropdown (enum in, integer out), first-class expires_at,
a /members/all default (direct-only opt-in), resource-type selector, and
member_role_id passthrough.

* test(gitlab): cover access operations

Covers the access_level enum-to-integer coercion, the /members/all default vs
direct-only, the 409-duplicate-add soft success, invitation per-email error
handling, user-status-action response parsing, and getGitLabResourcePath.

* docs(gitlab): document access and membership operations

* Update apps/sim/blocks/blocks/gitlab.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(gitlab): address review findings

- update_user now sends admin:false so the Administrator switch can demote
  (an untouched switch stays undefined and leaves the flag unchanged)
- expose the access-level dropdown for Update Invitation
- normalize comma-separated invite emails so spaced multi-email input works

* fix(gitlab): make update-invitation access level optional

Update Invitation now uses a dedicated dropdown that defaults to 'Leave
unchanged', so updating only the expiration no longer silently resets the
invitation's access level to Developer. The level is sent only when explicitly
chosen.

* fix(gitlab): validation pass — SAML provider param, member/invitation query filter, moderation user guard, registry order

* fix(gitlab): apply 10-agent validation findings across all 62 tools

- MR draft flag now applied via Draft: title prefix (GitLab has no draft body param)
- update_user only sends admin when a real boolean (untouched switch serialized null and could demote admins)
- add_member 409 soft-success now verified against the conflict body
- auto_merge sent alongside deprecated merge_when_pipeline_succeeds
- job log capped at 200k chars, file content at 1M chars, with truncated outputs
- MR diffs signal hasMore beyond 100 files
- wire dropped params: update_issue milestoneId, MR milestone/squash/removeSourceBranch, pipelines ref, tree ref, branches search, commits since/until/path/author, update_file lastCommitId, jobs includeRetried, create_user forceRandomPassword
- complete pipeline/job status enums, access-level enums, widen stale type unions
- guards: update_invitation requires a change; create_user requires a password strategy
- fix double-encoding trap in path descriptions; doc-accuracy touch-ups

* fix(gitlab): review round 1 — dedicated no-default access level for update member, expiration clearing via explicit empty string

* fix(gitlab): explicit Clear Expiration toggle for update member/invitation

* feat(gitlab): expose full documented API surface across tools and block

- membership: add-by-username, remove-member cleanup flags, list-member filters (user_ids/state/seat info), invite_source
- listings: search/visibility/owned/membership, assignee/milestone filters, source/target branch filters, per-domain order-by + sort direction
- CI: pipeline variables + spec:inputs, manual-job variables
- repo: commit authoring (start branch, author, execute flag), release tag message + asset links, cross-fork compare + unidiff, internal notes
- catalog: access-governance template + member-provisioning and access-request-audit skills
- hardening from 3-agent validation: declared release params, tolerate single-object asset links, NaN guard on assignee filter, null/scalar JSON rejection

* fix(gitlab): tri-state controls for update-op booleans (executable flag, MR squash/remove-source-branch)

---------

Co-authored-by: Marcus Chandra <mzxchandra@gmail.com>
Co-authored-by: mzxchandra <129460234+mzxchandra@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-07-16 12:00:12 -07:00
Theodore Li b5196a41a8 fix(mothership): keep chat pinned to bottom across multi-line input resizes (#5711) 2026-07-16 14:41:47 -04:00
f03b4337fa feat(mothership): mixture of models, search agent, persistent subagents, fork chat, inline questions (mothership v0.8) (#5410)
* feat(scout): add scout agent

* fix(contracts): update contracts to include scout agent

* feat(copilot): search agent (research+scout merge) + read-only table/KB tool handlers

Mirrors mothership dev f90f9b05:
- regenerated tool-catalog/tool-schemas mirrors (search trigger replaces
  research + scout; QueryUserTable / SearchKnowledgeBase entries)
- queryUserTableServerTool / searchKnowledgeBaseServerTool: read-only
  wrappers delegating to the full user_table / knowledge_base handlers with
  hard operation allowlists (and outputPath export rejection on
  query_user_table)
- display maps: 'search' agent label/title/icon added; research + scout
  entries retained so historical transcripts keep rendering
- Search.id replaces Research.id in LONG_RUNNING_TOOL_IDS (it inherits
  research's long crawls)

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

* feat(copilot): run_code compute-only handler; docs lint fix

Mirrors mothership dev db60da94: run_code is the compute-only variant of
function_execute for the search agent — same sandbox and inputs, no
outputs.files / outputTable, so it cannot create or overwrite workspace
resources. Wrapper handler hard-rejects the write vectors and delegates to
executeFunctionExecute; run_code is deliberately absent from
OUTPUT_PATH_TOOLS and the table output post-processor, so the name gating
blocks writes even for leaked args. Added to LONG_RUNNING_TOOL_IDS,
display title/icon maps, and the regenerated catalog/schema mirrors.

Also removes two ineffective biome suppression comments in the docs
workflow-preview (the rule doesn't fire in the docs app config).

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

* fix(copilot): failed tool calls must surface their error in terminal data

A failed handler result that carried a defined-but-empty output (the
app-tool executor's 'Tool not found' ships output: {}) won the priority
race in getToolCallTerminalData, so the resume payload's data — the only
thing the model reads — was a bare {} with the error text dropped. The
search agent retried run_code 20+ times blind against a stale server
because every failure rendered as empty instead of 'Tool not found'.

Failed calls now always carry error in their terminal data: merged into
object outputs, wrapped alongside non-object outputs, preserved when the
output already has an error field.

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

* feat(chat): render inline question tags from the agent in chat

* fix(chat): let inert multi-step questions browse all prompts

* improvement(chat): guard question answer formatting against sparse arrays

* chore(copilot): drop user_memory from generated contracts and tool display

Companion to mothership 8ae32e97 (user_memory tool removed — the feature no
longer exists). Regenerates the mothership contract mirrors via
generate-mship-contracts.ts, which also picks up the pending telemetry
contract additions (gen_ai.agent.name labels, llm.client.context_tokens,
llm.client.compactions, llm.request.compaction_trigger, llm.compaction.pause,
gen_ai.usage.context_tokens), and removes the user_memory display title.

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

* improvement(chat): answered question card becomes the user turn; two select types only

UI ordering: answering a question card no longer echoes a duplicate user
bubble. The combined answer still goes on the wire as a user message, but the
chat pairs it back to its card (strict 'Prompt — Answer' match, now uniform
for single questions too) and renders the card as the answered recap — the
card IS the user turn, and the next assistant message streams below it. The
pairing is derived from the transcript, so live and reloaded renders are
identical; a dismissed card followed by an unrelated typed message does not
match and renders normally. Messages ending with a question card also drop
the copy/thumbs actions row — the card is an input surface, not a reactable
assistant turn.

Question types are now single_select and multi_select only: text is removed
(the free-text 'Something else' row covers it) and confirm collapses into
single_select with Yes/No options. multi_select rows toggle with a check and
the free-text row's arrow submits the step; answers are comma-joined labels
plus any typed entry. Agent-supplied catch-all options ('Other', 'Something
else', 'None of the above') are stripped at parse — the card always provides
its own free-text row; a question left with no real options is invalid.

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

* improvement(chat): question cards are single_select only

Removes multi_select (and its toggle/check UI). The card is one shape: pick
one option or type into the always-present 'Something else' row. Catch-all
stripping and the transcript pairing/recap behavior are unchanged.

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

* improvement(chat): bring back multi_select question cards

Re-adds multi_select with a reworked interaction: option rows carry real
checkboxes (emcn Checkbox chrome) instead of numbers and arrows, an
option-styled Submit row confirms the step, and the "Something else" row
reads as a plain option until clicked — then it becomes the focused text
box, auto-checks, and can be unchecked without losing the typed text
(blur with nothing typed reverts it). single_select behavior, catch-all
stripping, and the transcript pairing/recap format are unchanged;
multi_select answers are the checked labels comma-joined.

* chore(copilot): regenerate mothership contract mirror (chat blob span attrs)

* chore(copilot): regenerate mothership contract mirror (chat blob metrics)

* feat(secrets): make output of generate api key a secret

* feat(cli): add mkdir, mv, cp to mship tool set

* feat(fork-chat): add fork chat to mothership

* fix(fork-chat): fix messageid handling in fork chat

* feat(credentials): agent-initiated oauth credential reconnect (#5488)

* feat(credentials): agent-initiated oauth credential reconnect

* fix(credentials): address reconnect review findings

* improvement(credentials): log when connect draft name lookups degrade

* fix(conflicts): remove migration

* fix(conflicts): fix conflicts

* fix(fork-chat): add migrations back

* fix(ci): fix lint

* fix(ci): fix bad import

* fix(vfs): fix 500 char limit in vfs for skills and custom tools

* feat(copilot): gate user skills to explicit slash-attach (#5536)

Stop the mothership from adopting a workspace user-skill on its own:

- Remove the load_user_skill tool and its three payload callers (chat
  payload, mothership execute route, inbox executor); delete
  lib/mothership/skills.ts + its test. Skills no longer autoload as the
  agent's own instructions.
- Rename the workspace "## Skills" inventory to "## Agent Block Skills
  — NOT FOR YOU" with a one-line guardrail so a skill's description
  (e.g. "respond like a pirate") is not treated as an instruction.
  Skills reach the model as behavior only via explicit /-attach.

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

* feat(lots-of-things): lots of things

* feat(subagents): add persistent subagents

* fix(copilot): let edit_workflow set knowledge-base tag filters, and stop it clearing them (#5546)

* fix(copilot): persist KB tag subblocks as JSON strings from edit_workflow

The edit_workflow tool normalizes array-with-id subblocks (via
normalizeArrayWithIds) but only re-stringifies the keys listed in
JSON_STRING_SUBBLOCK_KEYS. `tagFilters` (knowledge-tag-filters) and
`documentTags` (document-tag-entry) were missing, so agent-authored tag
filters were stored as raw JSON arrays while those UI components read
their value with JSON.parse (expecting a string). The result: an agent
edit to a Knowledge block's tag filter persisted correctly but rendered
as an empty filter in the editor (JSON.parse on an array throws -> []).

- Add `tagFilters` and `documentTags` to JSON_STRING_SUBBLOCK_KEYS so
  edit_workflow stores them in the same shape the UI writes.
- Make both components' parsers tolerate an already-parsed array on read,
  self-healing values already persisted in the broken (array) shape.

Search execution was unaffected (parseTagFilters accepts arrays), so the
value was never lost — only the editor render and round-trip were broken.

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

* feat(copilot): expose KB tag definitions in VFS meta.json

Surface each knowledge base's defined tags (displayName -> tagSlot) inline in
its meta.json via serializeKBMeta, loaded in one batched query
(loadKbTagDefinitions), so the agent can bind a knowledge-tag filter to a real
tag slot instead of guessing a tag name it cannot otherwise see.

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

* fix(copilot): stringify KB tag subblocks on the nested-node edit path

The nested-node merge path normalized array-with-id subblocks but never
re-serialized the JSON_STRING_SUBBLOCK_KEYS, so editing a block nested in a
loop/parallel container still persisted tagFilters/documentTags (and
conditions/routes) as raw arrays -- the exact shape the subblock components
cannot JSON.parse.

Route all four write paths through a single normalizeSubblockValue helper so
the normalize and re-stringify steps cannot drift apart again, and extract the
duplicated string-or-array read logic into parseJsonArrayValue.

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

* refactor(copilot): tighten subblock serialization helpers

Derive KbTagDefinitionSummary from the canonical TagDefinition instead of
restating its fields, make parseJsonArrayValue generic so callers drop their
`as T[]` casts, and unexport the three builders helpers that no longer have
consumers outside the module now that normalizeSubblockValue fronts them.

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

* fix(copilot): stop stripping tagFilters/documentTags from the agent's workflow view

sanitizeForCopilot dropped `tagFilters` and `documentTags` from the workflow state the
agent reads (workflows/{name}/state.json), while edit_workflow is allowed to write both.
The field was therefore write-only: on a follow-up edit the agent read back an absent
field, concluded no filter was set, and cleared the user's tag filter.

The redaction was introduced for workflow *export* (#1628) and is already enforced there
by sanitizeWorkflowForSharing's key list. The duplicate in the copilot-only
sanitizeSubBlocks was redundant for export and destructive for the agent. Removes it and
pins the contract with a regression test.

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

* fix(copilot): reject malformed KB tag values instead of clearing the filter

`knowledge-tag-filters` and `document-tag-entry` had no arm in the
`edit_workflow` input validator, so they fell through to the pass-through
default. Any non-array value the agent supplied -- a double-encoded JSON
string, an object, an unparseable string -- reached `normalizeSubblockValue`,
where `normalizeArrayWithIds` coerces unparseable input to `[]`. The write
path then persisted `"[]"` over the tag filter the user had configured.

`condition-input` and `router-input` already guard against exactly this and
return an actionable error to the model. Extend that arm to cover the two KB
subblock types. It keys on subblock type, so the unrelated `tagFilters`
short-input on the Algolia block is unaffected. `null`/`undefined` and empty
arrays still clear the field, so intentional clears keep working.

Also wrap `loadKbTagDefinitions` in try/catch. Tag definitions are an optional
meta.json enrichment, but the query ran inside the top-level `Promise.all`, so
a transient failure would reject the entire workspace VFS materialize and
leave the agent unable to read any file. Now it degrades to a meta.json
without tag definitions, matching the sibling materializers.

Adds regression tests for both, plus the first tests for
`parseJsonArrayValue`, the helper that keeps pre-fix raw-array rows readable.

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

* refactor(copilot): collapse duplicate JSON-array parsing in edit-workflow builders

`normalizeArrayWithIds` and `normalizeConditionRouterIds` each hand-rolled the
same "accept a raw array or the JSON string these subblocks persist" parse.
Extract `parseJsonArray`, which returns null when the value is neither, so each
caller keeps its own distinct fallback: `[]` for the former, the untouched
original value for the latter.

Behavior-preserving. An empty array is truthy, so `[]` and `"[]"` still parse
through rather than hitting either fallback.

`validation.ts` has a third copy, but `builders.ts` already imports from it, so
sharing the helper across the two would introduce an import cycle. Left as is.

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

* feat(copilot): specify tag name and legal operators in KB meta.json

`tagDefinitions` exposed `displayName`, but a `tagFilters` entry must carry the
key `tagName`. An entry written with `displayName` passes validation and
persists, then filters nothing -- a silent failure. Rename the field at the
serializer boundary; the DB column is untouched.

Also emit the operators legal for each tag's `fieldType`, reusing
`getOperatorsForFieldType`. `between` is valid for number and date but not for
text or boolean, and the agent has no way to infer that. An unrecognized
fieldType yields an empty list rather than throwing.

Still unspecified, and deliberately out of scope: a filter entry's value key is
`tagValue` (but `value` on documentTags), and `between` needs `valueTo`. Those
describe the subblock entry shape, not the knowledge base, so meta.json is the
wrong place for them.

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

* fix(copilot): pass a nullish subblock clear through instead of serializing "[]"

`validateValueForSubBlockType` accepts null as an explicit clear, but
`normalizeSubblockValue` then ran it through `normalizeArrayWithIds`, which
coerces any non-array to `[]`, and persisted the string "[]".

No data is lost either way -- "[]" and an absent field both mean "no filters".
But it left the field present when the caller asked for it to be unset, so
`sanitizeForCopilot` showed the agent an empty filter rather than an absent
one, contradicting the absent-means-unset invariant the sanitizer documents.
It also made Algolia's `if (params.tagFilters)` see a set value, since "[]" is
truthy.

An explicitly empty array still serializes to "[]" -- clearing with a value is
distinct from clearing by omission.

Reported by Cursor Bugbot on #5546.

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

---------

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

* feat(changes): huge changes

* fix(subagents): lanes

* fix(mship): transcript stuff

* fix(subagents): thinking lanes

* fix(superagent): fix superagent tools and checkpoints

* fix(scope): scope subagent tools

* fix(lint): fix lint

* chore(db): regenerate workspace_files.message_id migration as 0260 on staging base

* fix(superagent): fix superagent integration tools

* improvement(questions): make something else a placeholder

* chore(copilot): regenerate mothership contract mirror after staging rebase

* feat(mship): add external mcps to mship

* fix(ci): fix dev build

* fix(stream): show thinking text

* fix(ci): force redeploy

* fix(mothership): keep chat forks outside workspace storage billing

Preserve the product invariant that Mothership chat files are not charged as workspace file storage after the billing storage merge.

* fix(uploads): restore listWorkspaceFiles throwOnError option dropped in rebase

* fix(subagent-streaming): remove italics

* fix(mothership): treat subagent lanes closed by subagent_end as settled so the between-steps thinking indicator isn't suppressed

* fix(ui): thinking loader and rool names

* fix(ui): add file

* fix(thinking): show thinking during subagents

* fix(chat): drop dead thinking-channel ternary after lanes skip thinking blocks

* fix(thinking): remove thinking text

* improvement(function execute): add timeout to function execute and stop showing text in subagents

* fix(subagents): hide thinking text

* fix(ff): move ff to go

* improvement(superagent): nuke superagent

* feat(main-agent): superagent into main agent

* chore(db): regenerate workspace_files.message_id migration as 0262 on staging base

* fix(credentials): restore reconnect params on shared createConnectDraft

* fix(migrations): rebase with staging

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
Co-authored-by: Justin Blumencranz <96924014+j15z@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-07-16 11:24:46 -07:00
Waleed 68c4f28b77 feat(clickup): webhook triggers, hierarchy selectors, and Docs knowledge-base connector (#5708)
* feat(clickup): webhook triggers with auto-managed subscriptions + hierarchy selectors

* feat(clickup): KB connector (Docs v3), selector-route hardening, subblock migrations, registry-check regex fix

* test(clickup): webhook provider handler tests + redact create-response secret in error logs

* fix(clickup-connector): trim final page to maxDocs cap with precise listingCapped semantics

* chore(clickup): lint formatting

* fix(clickup): restore list-op location requiredness, split listSpaceId migration target, surface failed webhook rollback

* fix(clickup): clickup.lists selector accepts listSpaceId context like clickup.folders

* fix(clickup): integer-only maxDocs and location filters, depth-aware doc headings, most-specific-location hints
2026-07-16 10:17:02 -07:00
Waleed ff1d061897 feat(tools): Rocketlane integration — 64 tools across projects, tasks, phases, fields, time tracking, spaces, and invoices (#5709)
* feat(rocketlane): Rocketlane integration — 64 tools across projects, tasks, phases, fields, time tracking, spaces, and invoices

* fix(rocketlane): allow clearing optional fields on update, require a user reference for time-off and placeholder assignment

* chore(rocketlane): trigger fresh review round

* fix(rocketlane): require an owner reference when creating a project, matching the API contract
2026-07-16 10:10:19 -07:00
Waleed 2ec72b6c81 improvement(sidebar): prefetch workspace list server-side so the switcher never flashes loading (#5706) 2026-07-16 01:42:52 -07:00
Theodore Li f5a6c47f01 feat(start-block): add run metadata toggle with trusted <start.metadata> outputs (#5700)
* feat(start-block): add run metadata toggle with trusted <start.metadata> outputs

* fix(start-block): fail-soft email lookup, consistent nested metadata propagation, toggle parsing parity

* improvement(start-block): enumerate metadata fields in toggle description for agent schema

* fix(start-block): carry metadata chain through toggle-off children, preserve fail-soft null email

* fix(start-block): recover metadata chain from seeded start output after resume
2026-07-16 03:42:52 -04:00
Waleed d7f0a110fb feat(credentials): client-credentials service accounts — Zoom, Box, Salesforce + Pipedrive API tokens (#5690)
Second service-account kind (follow-up to #5682): client-credential pairs
(client ID + secret + org identifier) that mint short-lived tokens at
execution — in-memory cache with ciphertext-fingerprint validation
(rotation-correct across instances), single-flight coalescing, 30s failure
memo, no refresh-token storage.

- Zoom Server-to-Server OAuth, Box Client Credentials Grant, and Salesforce
  client-credentials (integration user) are the first minters; Salesforce's
  live instance_url rides the existing instanceUrl plumbing so all 40 tools
  work unchanged
- Pipedrive lands as a token-paste provider with explicit authStyle
  threading: descriptor declares x-api-token, one shared header helper drives
  all 18 tools + both selector routes, OAuth Bearer behavior untouched
- SSRF-allowlisted Salesforce My Domain host (production/sandbox/developer
  partitioned domains); ENOTFOUND maps to site_not_found, EAI_AGAIN stays
  provider_unavailable; 408/429 from token endpoints never blamed on creds
- Create route forwards clientId/clientSecret/orgId to the builder, pinned by
  a route-level regression test
- Zoom user-scoped tools document that server-to-server tokens don't support
  'me'
- 4 setup-guide docs pages verified against current vendor flows (incl.
  Salesforce External Client Apps — the classic Connected App wizard is
  disabled by default since Spring '26); integrations sidebar gains a Service
  Accounts & API Keys section
2026-07-16 00:41:50 -07:00
Waleed 3a632936ab feat(clickup): ClickUp integration — 23 tools, OAuth + API-token auth, attachment upload (#5702)
* feat(clickup): add ClickUp integration with OAuth + API-token auth, 23 tools, block, and attachment upload

- 23 tools covering tasks (create/get/update/delete/list/search), comments
  (create/get/update/delete), attachment upload, tags, members, custom
  fields, and the workspace/space/folder/list hierarchy
- OAuth provider wiring (authorization-code flow, non-expiring tokens) plus
  clickup-service-account token-paste credential (personal pk_ API tokens),
  with a shared clickupAuthorizationHeader helper (pk_ tokens sent bare,
  OAuth tokens as Bearer)
- File upload follows the internal-route pattern: contract-validated
  /api/tools/clickup/upload-attachment builds the multipart form and
  returns UserFiles
- ClickUp block with per-operation subBlocks, canonical file param,
  BlockMeta templates/skills, and gradient brand icon
- Generated integration docs page + hand-written service-account guide

* fix(clickup): apply validation-audit fixes across tools, block, and upload route

- Map documented task fields that were dropped: markdown_description,
  subtasks, watchers, custom_fields, time_spent, folder, space — making
  the include_subtasks / include_markdown_description options observable
- Expand verified filters: assignees/tags/due-date ranges on get_tasks and
  search_tasks, include_closed on search_tasks; add due_date_time /
  start_date_time flags and update-task assignee add/remove
- Guard update_comment against an empty body and require comment text in
  the block; prefer markdown_content over content on create_list and make
  markdown reachable for lists in the UI
- Drop the unverified 'required' field from custom-field outputs; read
  both err and error keys from ClickUp error bodies; correct notify_all
  wording
- Upload route: 100MB size cap, shared attachment mapper with full
  documented response fields (version, thumbnails), base-URL constant

* fix(docs): restore clickup-service-account guide and shield it from doc generation

The generator prunes integration pages it does not derive from blocks;
add the hand-written ClickUp API-token guide to
HANDWRITTEN_INTEGRATION_DOCS so regeneration cannot delete it.

* fix(clickup): address review findings — dedupe catalog entries, config-time list parent validation, upload memory cap, unique icon gradient ids

- Remove duplicated clickup entries in docs meta.json and integrations.json
  introduced by a double docs regeneration
- Add a Location dropdown for Get Lists / Create List so the folder ID or
  space ID is conditionally required at configuration time instead of
  failing at run time
- Pass the 100MB cap into downloadServableFileFromStorage so oversized
  files abort during download instead of after full buffering
- Use useId()-derived SVG gradient ids for ClickUpIcon in both icon files

* chore(clickup): format integrations.json entry per biome

* improvement(clickup): final validation-pass refinements across tools and block

- create_task: add doc-backed sprint points param (parity with update)
- get_tasks/search_tasks: expose include_markdown_description
- update_task legacy numeric priority in list responses mapped instead of
  dropped; create_comment omits absent response fields instead of
  emitting sentinel ''/0 values
- order_by only sent when explicitly chosen (Default sentinel); comment
  text no longer UI-required for update_comment (resolve-only and
  assignee-only updates are valid per the tool contract, which still
  rejects an empty body)
- add_tag_to_task sends no request body per docs; upload tool tolerates
  non-JSON error responses

* fix(clickup): tolerate nested user wrapper in member mapping

The task/list member endpoints document a flat member object; accept the
workspace-members-style nested { user: {...} } wrapper as well so both
shapes map correctly.

* fix(clickup): map size-limit errors from download/compile to a 400 upload-size response

downloadServableFileFromStorage enforces maxBytes on both the raw download
and the resolved (compiled) artifact via PayloadSizeLimitError; catch it in
the route so oversized content returns the intended 400 instead of
bubbling to the generic 500 handler.

* feat(clickup): add custom field values, checklists, and time tracking (15 tools, 38 total)

- Set/remove custom field values on tasks (PUT/DELETE /task/{id}/field/{field_id});
  block value input parses JSON for structured field types, plain values pass through
- Checklist CRUD: create/rename/reorder/delete checklists and create/update/
  delete checklist items (assign, resolve, nest), mapped from the documented
  {checklist} response shape
- Time tracking: list entries in a date range (assignee/location filters,
  task-tag and location-name includes), create/update/delete entries, start/
  stop timers, and read the currently running timer; entries mapped from the
  documented data envelope with negative-duration running semantics
- Block gains 15 operations with conditionally-required fields, timestamp
  wand configs, tri-state billable/resolved dropdowns, and a single-location
  filter selector matching the API's one-location-filter rule

* fix(clickup): new-tools audit fixes — POST for set custom field value, tolerant time-entry envelopes, richer mappings

- Set Custom Field Value uses POST per the live reference OpenAPI (the
  llms mirror shows PUT; the reference console spec is authoritative)
- delete_time_entry maps the documented array envelope; create_time_entry
  tolerates both data-wrapped and flat echo bodies
- Time entries surface task_tags and task_location so the include switches
  are observable; checklists carry date_created
- Custom field value input parses any JSON literal (numbers, booleans,
  arrays, objects) and passes plain text through
- Update Time Entry supports duration edits; single-assignee time ops get
  their own field so a comma-separated list can't silently NaN out

* fix(clickup): send explicit date-time flags whenever a date is set

The due/start date-time switches previously only transmitted true; a
timed date could never be flipped back to date-only. The flag is now sent
as an explicit boolean whenever the corresponding date is provided and
omitted otherwise.

* improvement(clickup): final per-tool audit polish — checklist item children, tolerant comment date

- Checklist items surface the documented children array of nested item IDs
- create_comment tolerates a string-typed date in the response

* fix(clickup): reject empty update_task bodies with a clear local error, matching sibling update tools
2026-07-16 00:38:59 -07:00
Vikhyath Mondreti 54b35a4f0e improvement(deployments): bugfixes for run-block, airtable + external sub management (#5680)
* improvement(webhooks): external subscription management

* ui/ux

* remove test file

* fix tests

* address comments

* address comments

* update to grain v2 api

* improvement(grain): hide auto-registered webhook URL on v2 triggers

* Revert "improvement(grain): hide auto-registered webhook URL on v2 triggers"

This reverts commit c89660cc3e.

* address comments

* address comments

* address rollback

* fix grain v2

* fix more comments
2026-07-15 18:01:53 -07:00
Theodore Li 4d6301c900 feat(pii): regex-only block-output redaction + drop GLiNER/GPU image (#5697)
* chore(pii): remove GLiNER/GPU image + add spaCy-skip fast path to CPU server

* feat(pii): restrict block-output redaction to regex-only entities

* fix(pii): derive spaCy-NER set from registry + skip fast path when score_threshold set

* fix(pii): include ORGANIZATION in app-side NER set (align with server)
2026-07-15 20:28:15 -04:00
Vikhyath Mondreti 3498b2475a fix(agent): pass through billing attribution to tools (#5698)
* fix(agent): pass through billing attribution to tools

* tests

* fix formatting

* address comments
2026-07-15 16:36:53 -07:00
Theodore Li bfe83869e8 fix(auth): recover cleanly when an impersonation session expires (#5692)
* fix(auth): recover cleanly when an impersonation session expires

* fix(auth): share stale-session recovery and bypass cookie cache in impersonation poll
2026-07-15 15:44:45 -04:00
e2ea49ea7e feat(instagram): add Instagram integration (#5568)
* feat(instagram): add Instagram Login OAuth, tools, and block

* feat(instagram): add Gmail-style media uploads for publish ops

Resolve UserFiles to Meta-fetchable presigned HTTPS URLs (600s TTL) via
internal publish routes, and fix OAuth scope storage plus connect-draft
wiring so Instagram Login publishing is testable end-to-end.

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

* refactor(instagram): simplify messaging tools to direct requests, clean up types

* fix(instagram): parallelize carousel child polling, enforce 2-10 items, extend poll window and insights periods, use canonical user_id in OAuth callback

* fix(instagram): resolve user id from user_id only, accept numeric user_id

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

* fix(instagram): use form/query params for publish and comment endpoints, request message timestamps explicitly

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

* fix(instagram): normalize Graph ID outputs to strings so downstream .trim() calls are safe

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

* style(instagram): use brand gradient tile for the block icon

Match the official Instagram look by filling the tile with the orange–pink–purple radial gradient so the white camera glyph sits on a full-bleed brand background.

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

* fix(instagram): tighten publish defaults and cloud-storage upload UX

Default Reel share-to-feed to Yes, drop unused media fields params, share publish transform helpers, and warn when cloud storage is missing for Meta-fetchable uploads.

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

* fix(instagram): fail closed when cloud storage status is unknown

Treat loading/error as blocked for requiresCloudStorage uploads, show the warning once the check finishes, and disable selecting local workspace files Meta cannot fetch.

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

* fix(instagram): proactively refresh long-lived tokens before expiry

Meta only allows refreshing still-valid Instagram tokens, so refresh within 14 days of expiry (after the 24h age gate) instead of waiting until after accessTokenExpiresAt.

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

* fix(oauth): restore TikTok clientIdParamName JSDoc after merge

The staging merge dropped the opening /** on ProviderAuthConfig.clientIdParamName, which broke TypeScript parse in CI.

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

* fix(api): Zod-contract storage-status and ratchet validation baseline

Wire /api/files/storage-status through a shared route contract so the
strict API validation audit stays at zero non-Zod routes after the new
Instagram cloud-storage check.

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

* fix(instagram): match Gmail advanced media placeholders

Drop public-URL paste hints from advanced fields and the cloud-storage banner so the UI mirrors Gmail attachments.

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

* code review + hide from toolbar

* address comments

* fix(instagram): drop hidden Instagram from OAuth catalog pin test

Instagram is hideFromToolbar so it is excluded from integrations.json;
the pinned slug map must not expect it until the block is visible again.

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

* fix(instagram): validate client ID before creating connect draft

Avoid orphan pending credential drafts when INSTAGRAM_CLIENT_ID is missing,
matching the Shopify authorize ordering.

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

---------

Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-07-15 11:33:45 -07:00
Waleed 85c3a7b3f6 fix(settings): align recently-deleted icon colors with platform token (#5691) 2026-07-15 11:11:50 -07:00
Waleed 7714663dae improvement(landing): SEO copy and metadata rewrite across marketing pages (#5689)
* improvement(landing): SEO copy and metadata rewrite across marketing pages

* improvement(landing): remove em dashes from marketing copy

* fix(landing): name Sim in the knowledge base H1
2026-07-15 10:30:57 -07:00
Waleed 4d0973fa9a fix(workspace): recover from stale sessions instead of blank loader after impersonation (#5688)
* fix(workspace): recover from stale sessions instead of blank loader after impersonation

* fix(workspace): gate stale-session recovery on auth state and surface failed sign-out

* fix(workspace): proceed with redirect when session query errors but cached identity exists
2026-07-15 10:24:45 -07:00
Waleed 64280c955e feat(landing): compare footer column, sales solutions page, nav reshuffle, scheduled-tasks calendar hero (#5684) 2026-07-14 23:35:17 -07:00
Waleed eac7bf0bfd fix(library): correct unlimited-execution wording to acknowledge tunable admission defaults (#5685) 2026-07-14 23:33:57 -07:00
Waleed c955338bef feat(library): add Apache 2.0 vs fair-code licensing article (#5683) 2026-07-14 23:12:05 -07:00
Waleed 00a2f26fe5 feat(credentials): token-paste service accounts for 12 providers (#5682)
* feat(credentials): token-paste service accounts for 12 providers (HubSpot, Airtable, Notion, Asana, Attio, Linear, monday, Shopify, Webflow, Trello, Cal.com, Wealthbox)

* refactor(credentials): registry-dispatch service accounts + doc-verified validator hardening

- Migrate Google/Atlassian/Slack service-account branches to the same
  registry pattern as token-paste providers (builder + resolver maps,
  unified required-fields contract validation)
- Fix Shopify service-account store domain never reaching tool URL builders
- Linear: rate-limit 400s no longer mislabeled as invalid credentials;
  validation header matches runtime header rule
- Trello: server API key rejection no longer blamed on customer token
- Fleet-wide: network errors and non-JSON provider bodies map to
  provider_unavailable via shared fetchProvider/parseProviderJson
- HubSpot: drop regional-host displayName upgrade; Wealthbox: explicit 402;
  monday: provider-side GraphQL errors mapped correctly; Attio: null-body guard

* polish(credentials): ship-gate fixes, setup-guide docs, and lint pass for token service accounts

- 12 setup-guide docs pages (docs.sim.ai/integrations/<provider>-service-account)
  incl. the Trello authorize-link flow the integration depends on
- Attio: HTTP 400 on /v2/self maps to invalid_credentials (live-verified)
- monday: scan all GraphQL errors for provider-side codes; no empty audit ids
- Compile-time descriptor/validator lockstep (typed registry) and
  prototype-safe provider-id guard (Object.hasOwn)
- Shared errors.test.ts pinning fetchProvider/parseProviderJson/
  throwForProviderResponse guarantees for all validators
- Stale TSDoc updated after the registry migration; biome organizeImports pass

* docs(credentials): vendor-doc accuracy pass on service-account setup guides

34 corrections from a 12-agent audit against live official vendor docs:
current HubSpot Development-area nav, Notion connections rename, monday
Developer Center paths, Shopify legacy-vs-Dev-Dashboard token flows, and
hedged wording for claims vendors do not document (expiry, limits)

* improvement(credentials): vendor-accurate credential nouns on connect surfaces

'Add service account' only where the vendor actually has service accounts
(Google, Atlassian); token-paste providers now use their own vocabulary via a
connectNoun descriptor field — 'Add private app token' (HubSpot), 'Add API key'
(Attio/Linear/Cal.com), 'Add personal access token' (Airtable), etc. Docs page
titles updated to match; slugs and internal provider ids unchanged

* fix(credentials): classify auth-shaped Shopify GraphQL errors as invalid credentials

Shopify can reject invalid or revoked shpat_ tokens with HTTP 200 and a
GraphQL error body instead of a 401; those now map to invalid_credentials
instead of a provider-outage message

* fix(credentials): empirically-grounded HubSpot token verification

Live probing showed the documented access-token-info route returns a bare
404 for unrecognized tokens (ambiguous with a missing route), so 404/400
now falls back to the Account Information API, which answers with a JSON
401 for rejected tokens and 200/403 for live ones — verified against the
real endpoints

* fix(credentials): review-round-2 fixes for service-account edge cases

- Shopify tools prefer the credential-validated store domain over the
  block's auto-detected shopDomain (a store-bound token must hit its own store)
- Unknown non-empty service-account providerIds are rejected instead of
  silently persisting as google-service-account (empty stays the legacy
  Google fallback)
- Shopify/modal domain normalization strips URL paths ('https://x.myshopify.com/admin')
- monday: warning-class GraphQL errors no longer reject a token whose me
  data proves it authenticated

* chore(credentials): format shopify validator test

* fix(credentials): cold-review hardening pass

- Object.hasOwn guards on all provider-id registry lookups (crafted
  '__proto__'/'constructor' providerIds now 400 instead of 500) + regression test
- fetchProvider gets a 10s AbortSignal.timeout so a hung provider can't pin
  the create/reconnect request
- HubSpot: unexpected 403 on the token-info route defers to the account-info
  fallback instead of blaming the token
- Linear selector routes use the SDK apiKey option for lin_api_ keys (bare
  header parity with the tools sweep)
- Docs: UI steps aligned to the vendor-noun connect labels; HubSpot
  scope-propagation claims softened; Trello in-product-link promise corrected
2026-07-14 22:52:50 -07:00
Waleed 92549844c0 improvement(landing): fix SEO redirects/schema, add FAQ content (#5681)
* fix(docs): remove Ask AI widget

* improvement(landing): add visible FAQ schema, fix SEO redirects and Organization schema

- Move existing plain-markdown FAQ sections in 8 library posts + emcn blog
  into the faq frontmatter array so they emit FAQPage JSON-LD (previously
  visible text with zero structured data)
- Add new, fact-checked FAQ content to the 8 posts with none: openai-vs-n8n-vs-sim,
  v0-5, enterprise, copilot, executor, multiplayer, mothership, series-a
- Add sales ContactPoint to the site-wide Organization schema
- Add 301 redirects for legitimate crawler/dead-link 404s (/$, /&, /Sim,
  /homepage, /logo, /en-US) surfaced by an external SEO audit; left out
  bot-noise paths, already-fixed OG image 404s (#5636), and *.sim.ai
  customer chat subdomains that need manual DB verification, not a
  blanket redirect

* revert(docs): restore Ask AI widget

Ask AI removal is a separate change, out of scope for this PR — restoring
the component, chat panel, api/chat route, and its deps to their
pre-existing state.

* fix(content): correct stale/mislabeled competitor facts surfaced in FAQ audit

- n8n's license is "fair-code" (n8n's own coined term), not "fair-use" —
  a different legal concept entirely; fixed 4 occurrences
- Activepieces' integration count is 750+ per their live catalog, not the
  280+ figure carried over from an older snapshot; fixed 4 occurrences
  across best-zapier-alternatives and n8n-alternatives
2026-07-14 19:53:56 -07:00
516dd7ed0f feat(landing): extend enterprise product-preview design to solutions and workflows pages (#5672)
* feat(landing): extend enterprise product-preview design to solutions and workflows pages

Parametrize enterprise feature graphics with content props and retell each
solutions page's feature blocs for its domain (engineering, it, compliance,
finance, hr) plus the workflows platform page. Add five new graphics in the
same visual vocabulary, animated platform-UI heroes with domain-specific
loops on all six pages, hero category tags, container-query proportional
tile scaling with wide/2x2 layout options, and a shared pre-footer CTA band.
Enterprise page renders identically.

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

* feat(landing): surface Platform and Solutions menus in top nav

Renders PLATFORM_MENU and SOLUTIONS_MENU alongside Resources (Platform,
Solutions, Resources trigger order), drops the internal-only Mothership
item, centers the five-item Platform panel's two-tile last row, and lets
the mobile sheet scroll now that all three sections outgrow a phone
viewport.

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

* feat(landing): add scheduled tasks page and nav item

Adds the /scheduled-tasks landing page (SolutionsPage consumer with a
chat-free schedule-trigger editor hero and enterprise feature tiles
retold for recurring runs) and a sixth Scheduled Tasks item in the
Platform menu, restoring the clean 3x2 grid.

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

* feat(landing): add knowledge, tables, files, and logs platform pages

Completes the Platform nav dropdown — its knowledge, tables, files, and
logs items previously 404'd. Each page is a SolutionsPage consumer with a
product-UI hero loop, enterprise-style feature tiles, CTA band, and SEO
metadata. Removes the dead platform-page component family (workflows was
its last consumer) and adds the new routes to the sitemap.

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

* refactor(landing): consolidate hero-loop engine and code-window graphics, drop dead variant surface

- Extract shared useDesignScale + useMotionSafeCycle hooks and a HeroLoopShell
  so all seven hero loops share one scale/reduced-motion clock engine
- Merge workflows-editor-loop and scheduled-tasks-hero-loop into one
  parameterized shared EditorLoop (content-injection, same pattern as
  EnterpriseLoopContent)
- Consolidate the four identical code-window feature graphics into one shared
  CodeWindowGraphic + single CSS module; pages keep thin data wrappers
- Remove the unreachable 'split'/'standard'/align variant surface from
  SolutionsPage/SolutionsCardRow/SolutionsCard and trim unused barrel re-exports
- Enterprise hero now consumes the shared PlatformHeroVisual instead of an
  inline byte-identical copy
- Add /scheduled-tasks to the sitemap
- a11y: closed mobile-nav sheet is now invisible (out of tab order) with
  aria-controls wiring; decorative tables-hero checkboxes are no longer
  keyboard-focusable
- memo() the static EnterpriseSidebar; hoist per-render header arrays; remove
  comments that restated component TSDoc

* fix(landing): highlight Scheduled tasks in the scheduled-tasks hero sidebar

Thread activeNav through the shared EditorLoop content so the
scheduled-tasks hero highlights its own module row, matching the other
platform heroes.

* chore(landing): drop spacing constants orphaned by the variant removal

Remove the now-unconsumed cardRowHeaderStack/cardRowHeaderCtaGap/
cardTextToVisual/rowSubtitle constants, fix a stale cardVariant TSDoc
reference, and restore import order in the workflows editor-loop wrapper.

* chore(landing): single-source RESET_FADE_MS and drop the unused card frame size

RESET_FADE_MS now lives only in the shared loop-engine hook module; the
enterprise stage-data copy is removed. SolutionsVisualFrame loses its
size prop and cardHeight constant - only the 16:9 hero preset remained
in use after the split-variant removal.

* fix(landing): make the knowledge-answer question bubble visible on light tiles

The frameless vignette sits directly on the tile's --surface-3 fill, so
the build tile's --surface-3 bubble treatment vanished. The question
chip now uses the graphic family's white card chrome (--white fill,
--border-1 hairline), matching its own source card. Also restores
biome's import order in the workflows editor-loop wrapper (fixes the
failing lint:check CI step).

* chore(landing): share one ZipIcon across the files preview and files hero

Extract the duplicated zip-archive SVG into components/shared/zip-icon
and consume it from both the homepage files preview and the files hero
loop.

---------

Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Waleed Latif <walif6@gmail.com>
2026-07-14 17:38:19 -07:00
Waleed c203f51545 fix(tools): resolve {{ENV_VAR}} references in user-only params for copilot tool executions (#5679)
* fix(tools): resolve {{ENV_VAR}} references in user-only params for copilot tool executions

Chat-invoked integration tools with API-key auth have been broken since the
mothership tool-dispatch rewrite (#4090) removed the orchestrator's env
reference resolution. Agents pass {{VAR}} references (the VFS exposes env var
names only), and the literal placeholder was sent to the provider, producing
auth failures like Sentry's 401 Invalid token.

Resolution is deliberately narrower than the removed deep resolver: only
whole-value references on params declared visibility user-only, gated to
copilot executions, resolving against the same personal+workspace env merge
workflow runs use. LLM-writable params (urls, headers, bodies) never resolve,
so references cannot be used to extract secrets. Missing variables fail fast
with an actionable error instead of a provider-side 401.

* refactor(tools): delegate copilot env reference resolution to the shared executor resolver

Replaces the hand-rolled exact-match regex with resolveEnvVarReferences
(allowEmbedded: false), the same resolver used by workflow runs, MCP config,
and webhooks — one set of reference semantics instead of two that can drift.
Behavior is identical; all existing tests pass unchanged.

* fix(tools): fail fast on env references without user context, clarify personal-only scope errors

Addresses review: a copilot execution missing userId now errors explicitly
instead of forwarding the literal placeholder upstream, and a missing-variable
error without a workspace context explains that only personal variables are
in scope there (matching workflow-run resolution semantics).
2026-07-14 16:38:42 -07:00
Waleed 515dafa274 feat(billing): allow programmatic workflow execution on the free plan (#5678)
* feat(billing): allow programmatic workflow execution on the free plan

Remove the free-plan paywall on programmatic execution (API-key/public
execute, MCP serve, generic webhooks, cross-origin chat embeds) added in
#5036. The gate shipped dark behind FREE_API_DEPLOYMENT_GATE_ENABLED to
curb bot abuse; with that abuse handled upstream, free workspaces can use
the API again (still subject to the free-tier rate limits).

- Delete isWorkspaceApiExecutionEntitled / api-access.ts and the
  FREE_API_DEPLOYMENT_GATE_ENABLED env flag
- Ungate the execute, mcp/serve, and webhooks/trigger routes and the chat
  embed check (assertChatEmbedAllowed removed)
- Remove the deploy-modal upgrade wall (DeployUpgradeGate) and the now
  unused useWorkspaceOwnerBilling client hook; the owner-billing endpoint
  stays as reusable billing infrastructure
- Restore pre-gate upgrade-page copy (Pro feature line, free API endpoint
  rate-limit column)

* chore(billing): drop orphaned owner-billing endpoint and dead chat logger

Self-review follow-ups: the /api/workspaces/[id]/owner-billing route +
contract existed solely for the deploy-modal gate's client hook (deleted
here); host-context serves the same ownerBilling data server-side, so the
standalone endpoint is dead HTTP surface. workspaceOwnerBillingSchema and
the WorkspaceOwnerBilling type stay (embedded in workspaceHostContextSchema).
Also removes the now-unused ChatAuthUtils logger.

* style: biome formatting after gate removal
2026-07-14 16:33:11 -07:00