mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
369eef742f9b4d5f84f952811663bfaa7423d048
5509
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
369eef742f |
feat(library): add BYOK multi-model AI agent builder post (#5758)
* feat(library): add BYOK multi-model AI agent builder post * fix(library): scope BYOK FAQ to providers in the actual BYOK contract |
||
|
|
1b06b6c60d |
fix(security): bound YAML alias expansion in file-parser (billion-laughs DoS) (#5756)
* fix(security): bound YAML alias expansion in file-parser to prevent DoS The YAML parser called yaml.load() then JSON.stringify() on the result. YAML aliases (*anchor) resolve to shared references, so yaml.load cheaply builds a compact DAG, but JSON.stringify expands that DAG into a full tree, duplicating every shared node. A crafted sub-1KB .yaml/.yml document could therefore expand to hundreds of MB / GB during serialization, pinning CPU and OOM-killing the shared parse/ingestion worker (CWE-776). Add a bounded, iterative traversal that runs before JSON.stringify and aborts once expanded node count, estimated serialized size, or nesting depth exceeds safe caps. This detects the amplification against the compact DAG before any allocation, and also terminates cyclic anchor structures. Replaces the unbounded recursive getYamlDepth (which spread large arrays into Math.max(...array), risking a stack overflow) with the same bounded walk. * harden YAML guard: charge keys + escaping, bound stack, fail closed Addresses review findings on the alias-expansion guard: - Charge object keys, not just values. JSON.stringify re-emits every key on each alias expansion, so an aliased object with a long key amplified without being counted. Keys are now charged against the size cap. - Compute the exact JSON-escaped string length (quotes, backslashes, control chars) instead of a flat multiplier. Plain text is charged its true 1:1 size (no false rejection of large legitimate documents) while escape-heavy strings are charged their real, larger cost (no cap bypass). - Count each node as it is enqueued and only push container nodes onto the traversal stack. A pathologically wide fan-out now trips a cap during the enqueue loop instead of first materializing millions of stack entries and exhausting memory inside the guard itself. - Fail closed in POST /api/files/parse: a YamlComplexityError rejection is now re-thrown (mirroring isPayloadSizeLimitError) rather than silently falling back to storing the crafted document as raw text. * yaml guard: charge lone surrogates at their escaped length serializedStringLength treated surrogate code units as cost 1, but well-formed JSON.stringify escapes a lone surrogate to \uXXXX (six units). Charge lone surrogates as 6 and valid high+low pairs as-is (two units), matching JSON.stringify exactly. Verified against JSON.stringify across plain text, escapes, control chars, lone high/low surrogates, and valid pairs. |
||
|
|
99f2ca7995 |
feat(library): add Best Relay.app Alternatives in 2026 post (#5755)
* feat(library): add Best Relay.app Alternatives in 2026 post * improvement(library): clarify Sim free tier has usage limits in comparison table |
||
|
|
c739dd6b1e |
improvement(mcp): align tool discovery timeouts and retries with MCP protocol standard (#5753)
* improvement(mcp): align tool discovery timeouts and retries with MCP protocol standard - Raise tools/list idle timeout 10s -> 30s (derived from per-server config, clamped to max execution timeout) with a 60s hard cap via maxTotalTimeout, matching the SDK's DEFAULT_REQUEST_TIMEOUT_MSEC. Enable resetTimeoutOnProgress with an onprogress handler so slow-but-alive servers are not spuriously failed. - Retry read-only tools/list on transient transport errors (timeout, connection-closed, 429/5xx, session 404/400, network) with jittered backoff. tools/call stays conservative (session-error retry only) since it is not idempotent. The MCP SDK does not retry POST requests, so the app owns this. - Wire client.onerror so out-of-band transport failures are observed, not silently dropped. - Map timeouts to a user-facing message and surface refresh/remove failures via the standard emcn toast instead of swallowing them. * chore(mcp): trim comments to match codebase density * fix(mcp): don't fail refresh with 500 when post-discovery workflow sync throws Discovery already persists status and caches tools; a syncToolSchemasToWorkflows failure was escaping the refactored try/catch and returning 500 despite the refresh having succeeded. Guard the secondary sync so it degrades to zero workflows updated instead. * fix(mcp): keep tools/list absolute timeout ceiling hard A configured per-server timeout above 60s was expanding maxTotalTimeout past the intended absolute discovery ceiling. Clamp the idle timeout to the ceiling too so tools/list can never hang the UI beyond it; connect() and callTool() still honor the full configured/execution timeout. |
||
|
|
87edbc5d54 |
perf(mothership): restore static markdown parse for settled chat messages (#5751)
* perf(mothership): restore static markdown parse for settled chat messages Settled/reloaded chat messages rendered through Streamdown's streaming parser (remend + incomplete-markdown repair + per-block re-parse, running the rehype raw/sanitize/harden chain once per block). Because rows are virtualized, every up/down scroll remounted the messages crossing the overscan boundary and re-paid that N-block cost — the scroll lag. - Render never-streamed mounts (reloaded history, or an in-session message scrolled out of the virtualized window and back) with mode='static': one whole-document parse instead of streaming's per-block re-parse. In-session streaming keeps the streaming parser for its mount life (no drain flash). - Cache Prism highlight output in a module-level bounded LRU so a code block re-highlights at most once across the unmount/remount virtualization does on scroll (a component useMemo would not survive the unmount). * fix(mothership): don't cache fallback-highlighted code blocks An unregistered language highlighted via the JavaScript fallback was cached under its own name, so if that Prism grammar registered later in the session a remount would keep serving the stale fallback render. Resolve the grammar inside the highlight helper and skip the cache entirely on the fallback path. |
||
|
|
a4e2fb94ac |
feat(admin): show recently impersonated users in admin panel (#5750)
* feat(admin): show recently impersonated users in admin panel * improvement(admin): align user table with settings list idiom * fix(admin): address review findings on recent impersonations * fix(admin): exact server-side email filter for recent impersonation lookup |
||
|
|
c9baae7f2e |
fix(mcp): recover cleanly from OAuth failures (#5595)
* fix(mcp): improve OAuth failure recovery * fix(mcp): address OAuth recovery review findings * fix(mcp): prefer static bearer auth in connection tests * fix(mcp): preserve discovery failure state Treat static-header 401s as credential failures, keep OAuth failures pending, and prevent failed refreshes or reflected upstream errors from masquerading as connected state. |
||
|
|
7e975e7dae |
fix(confluence): index mirrored/included page content via rendered view format (#5746)
* fix(confluence): index mirrored/included page content via rendered view format The KB connector fetched page bodies as body-format=storage, which only carries unexpanded macro references (Include Page / Excerpt Include). Those 'mirrored' articles were stripped to empty content by htmlToPlainText and never synced. Switch getDocument to body-format=view (supported on the v2 single-item page/blogpost GET) so built-in include/excerpt macros render inline and the included text is indexed. * fix(confluence): invalidate existing doc hashes on representation change The version-based contentHash meant already-synced mirrored documents (with stale empty content) classified as 'unchanged' and never re-hydrated with the new rendered view content. Embed a body-representation marker in the hash so a representation change invalidates every previously-synced Confluence document, forcing a one-time re-hydration that picks up the expanded include/excerpt text. * feat(connectors): full resync re-hydrates rendered content (transclusions) Version-based change detection can't see when a Confluence page's rendered view changes because an *included* page was edited (the container's version doesn't bump). Add a 'Full resync' path so that drift can be recovered: - ConnectorMeta.rehydrateOnFullSync flag (set for Confluence) - on fullSync, classifyExternalDoc promotes unchanged deferred docs to update and the hydration guard re-indexes unconditionally, so rendered content is refreshed - fullSync threaded through the manual sync contract (query param), route, and hook - 'Sync now' / 'Full resync' dropdown on the connector card Incremental syncs stay hash-gated and cheap; only the deliberate full resync pays the re-index cost. * refactor(connectors): decouple rehydrate from fullSync deletion semantics The transclusion refresh only needs re-hydration, but reusing the fullSync flag also activated its deletion-cleanup semantics — which bypass three previously unreachable safety guards (empty-listing wipe, listingCapped, and the >50% mass-deletion threshold). Since fullSync had no caller before this PR, the new 'Full resync' button would have exposed all three to any KB editor. Introduce a dedicated 'rehydrate' request that ONLY forces re-hydration + re-index of already-synced docs. Listing and deletion reconciliation are identical to a normal sync (all safety guards stay armed). fullSync's cleanup semantics remain dormant and untouched. * refactor(connectors): tidy rehydrate flag + gate Full resync to supported connectors Cleanup/simplify pass over the connector changes: - use shared booleanQueryFlagSchema for the rehydrate query param (typed boolean at the boundary instead of a hand-rolled 'true'/'false' string enum) - move rehydrateOnFullSync onto the client-safe ConnectorMeta so the UI can gate on it - only Confluence (rehydrateOnFullSync) shows the Sync now / Full resync dropdown; every other connector keeps its original one-click sync button (Full resync is a no-op for them, and this restores the pre-change one-click UX) - wrap the sync trigger in a span so its tooltip still shows while disabled (cooldown) * fix(connectors): forward rehydrate flag through the Trigger.dev worker executeConnectorSyncJob (the production async sync path) destructured only fullSync from the payload and forwarded only fullSync to executeSync, silently dropping rehydrate. A manual Full resync would therefore never re-hydrate on the default Trigger.dev path. Forward rehydrate too. * fix(connectors): rehydrate forces a full listing so containers aren't omitted A rehydrate request set forceRehydrate but left listing incremental. For a connector that is both incremental and rehydrateOnFullSync, an unchanged container page that transcludes a changed page would be omitted from the incremental listing and never re-hydrated. Force a full (non-incremental) listing on rehydrate so every document is seen; deletion-safety guards stay armed (unlike fullSync). No-op for Confluence, which is already non-incremental. |
||
|
|
d637cbaac1 | improvement(admin): remove included usage for enterprise provisioning (#5749) | ||
|
|
4c86757380 | fix(billing): point enterprise Talk to sales at Cal.com instead of Typeform (#5748) | ||
|
|
13aaf03152 |
feat(billing): show invoice descriptions and cap in-app list at 5 (#5747)
* feat(billing): show invoice descriptions and cap in-app list at 5 * test(billing): model six-item Stripe page boundary in pagination mocks * fix(billing): decouple Stripe scan page size from invoice display cap |
||
|
|
b2d9c02bb9 |
fix(chat): hosted key execution via call-integration-tool (#5745)
* fix(chat): hosted key execution via call-integration-tool * fix optional hosted key |
||
|
|
ec75084dbf | fix(billing): standardize credit-usage summary font to proportional figures (#5744) | ||
|
|
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>
|
||
|
|
772b710e39 |
fix(mothership): canonical folder ids (#5741)
* folder fix * fix bug |
||
|
|
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. |
||
|
|
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 |
||
|
|
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 |
||
|
|
c9399f8413 | fix(oauth): serialize version-guard date in slack fan-out sql (#5737) | ||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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. |
||
|
|
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 |
||
|
|
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 |
||
|
|
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)
|
||
|
|
86e6e1d26c |
feat(forking): excluded workflows (#5727)
* feat(forking): excluded workflows * improve sync preview |
||
|
|
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 |
||
|
|
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 |
||
|
|
1da346b703 | feat(triggers): service-account credentials in the hubspot trigger + token-SA name-collision 409 (#5693) | ||
|
|
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 |
||
|
|
01ffacc2ed | improvement(chat): give wsres resource links the clickable chip treatment (#5719) | ||
|
|
db85ae998f | fix(chat): align resource mention icon spacing with mention chips (#5718) | ||
|
|
a218ebe4ed | fix(sidebar): align workspace header loading-state spacing with chip geometry (#5717) | ||
|
|
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 |
||
|
|
2f9144ebe3 |
improvement(ci): timeouts, docs-only PR skip, fork-isolated caches, Node pin; remove i18n workflow (#5714)
* improvement(ci): job timeouts everywhere, docs-only PR skip, event-scoped sticky disks, Node pin; drop dead i18n workflow - timeout-minutes on every runnable job (defaults ran hung jobs to the 6-hour cap — the i18n workflow burned three full 6-hour runs in Feb before its schedule was pulled) - paths-ignore on the pull_request trigger: docs content and markdown don't affect the app build or images; push runs stay unfiltered - sticky-disk keys scoped by event name so fork PR runs never share a disk with the push runs that feed production image builds - node-version pinned to 22 (was 'latest', non-deterministic) - delete i18n.yml: schedule already removed after repeated 6-hour hangs, workflow_dispatch-only since, comments stale * improvement(ci): 45m migrate timeout (covers 30m lock wait), fork-namespaced PR sticky disks - migrate.ts waits up to 30 minutes for the migration advisory lock (LOCK_ACQUIRE_DEADLINE_MS); the 15m job timeout would preempt that designed wait, so the bound is 45m - fork PRs now get their own sticky-disk namespace so an untrusted fork run can't poison the disks that trusted internal-PR runs restore |
||
|
|
46f6a9ba77 |
improvement(ci): promote superseded first-attempt runs forward (#5713)
* improvement(ci): promote superseded first-attempt runs forward The stale-promotion guard skipped any run whose commit was no longer the branch head. If commit A passed its gate but was superseded mid-run by commit B, and B then failed tests, A's promotion was skipped and the deploy tags stayed on pre-A code with no automatic recovery (Cursor finding on #5712). A first-attempt run promoting an ancestor of the branch head is always a forward deploy — runs on a ref are serialized by the concurrency group, so nothing newer can have promoted first. Only re-runs of superseded commits (a rollback attempt) and force-pushed-away commits are skipped. Same semantics applied to the GHCR latest-tag guard. * fix(ci): grant contents:read to create-ghcr-manifests for the compare-API guard Job-level permissions replace the workflow defaults, so packages:write alone left the guard's compare call 403ing — STATUS=unknown would have silently skipped the GHCR latest tags on every main push. |
||
|
|
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> |
||
|
|
b5196a41a8 | fix(mothership): keep chat pinned to bottom across multi-line input resizes (#5711) | ||
|
|
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> |
||
|
|
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 |
||
|
|
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 |
||
|
|
2ec72b6c81 | improvement(sidebar): prefetch workspace list server-side so the switcher never flashes loading (#5706) | ||
|
|
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 |
||
|
|
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 |
||
|
|
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
|
||
|
|
db9f165805 |
fix(babysit): resolve merge conflicts against staging during the review loop (#5695)
* fix(babysit): resolve merge conflicts against staging during the review loop * fix(babysit): trim merge-conflict handling to essentials * fix(babysit): gate conflict-resolution pushes and handle UNKNOWN mergeable * fix(babysit): bound persistent UNKNOWN mergeable state and clarify step skip * fix(babysit): merge instead of rebase to resolve conflicts, drop force-with-lease * fix(babysit): don't skip pending review findings when resolving a merge conflict * fix(babysit): spot-check commit hygiene before a merge-conflict push * fix(babysit): commit merge-conflict fixes before pushing, bound UNKNOWN before waiting * fix(babysit): run pre-push checks before committing the merge-conflict fix * fix(babysit): bound persistent CONFLICTING state and fix pre-push check order * fix(babysit): reconcile hard rule with step 2's merge-conflict sync exception * fix(babysit): page all review threads before branching on mergeable state * fix(babysit): cut merge-conflict handling down to the essentials * fix(babysit): simplify merge-conflict handling to a minimal step |
||
|
|
4bb560073f |
improvement(ci): decouple image builds from the test gate (#5701)
* improvement(ci): decouple image builds from the test gate - build-amd64 now starts immediately and pushes only sha tags (ECR :sha, GHCR :sha-amd64). The EventBridge deploy triggers filter on exactly the latest/staging/dev ECR tags, so nothing deploys from these pushes. - New promote-images job retags sha -> latest/staging (and GHCR latest-amd64/version-amd64) via buildx imagetools once tests and migrations pass — a seconds-long manifest copy instead of rebuilding after the gate. Cuts push-to-deploy from ~13.5 to ~7 minutes. - Split the Next.js production build out of test-build into a parallel Build App job with its own sticky-disk keys, cutting PR feedback from ~5.5 to ~3.5 minutes. - create-ghcr-manifests and process-docs now gate on promote-images. * improvement(ci): harden promotion — atomic retag, stale-run guards, gate all mutable GHCR tags Review follow-ups: - promote-images is a single job (not a matrix): verifies all four :sha manifests exist before moving any deploy tag, so a missing image can't cause a partial mixed-version deploy - stale-run guard on promote-images and create-ghcr-manifests: re-running an old run no longer retags latest/staging back to stale code (a one-click prod rollback); superseded runs skip mutable tags with a warning while immutable sha/version tags still publish - ARM64 build now pushes only the immutable :sha-arm64 tag; latest-arm64 and version tags moved behind the gate into create-ghcr-manifests (closes the pre-existing hole where a failing run moved latest-arm64) - dropped dead detect-version needs from both build jobs - sticky-disk comment corrected (clone + last-writer-wins, not exclusive mounts); build job shares warm bun/node_modules disks, keeps its own turbo cache key so test/build entries don't evict each other |
||
|
|
58e4b754f9 | fix(emcn): remove brand-accent focus ring from TagInput default variant (#5696) |