v0.7.64: snowflake, dynatrace, mintlify, files and security hardening, perf improvements - #6460
v0.7.64: snowflake, dynatrace, mintlify, files and security hardening, perf improvements#6460waleedlatif1 wants to merge 63 commits into
Conversation
…6386) * fix(jsm): accept the numeric pagination the JSM tools actually send The JSM tools declare start/limit as type: 'number' and the block coerces Max Results with Number.parseInt, but every /api/tools/jsm/* contract typed them as z.string(). Any JSM read with pagination filled in 400'd before reaching Atlassian, and get_queues 400'd unconditionally because the block always sends includeCount as a boolean. Normalize both shapes at the contract boundary, add the missing Start Index block input, and route Max Results through the existing toOptionalInt helper so a non-numeric entry no longer sends NaN. * improvement(forking): widen the fork mapping target picker further 320px still clipped the longest secret keys the picker shows. * fix(jsm): cap pagination at the documented int32 maximum Addresses review: the schema claimed the int32 range but only floored at 0, so values above 2147483647 were forwarded to Atlassian instead of being rejected at Sim's boundary. Also restores the const tuple for the paginated operation list and drops the widened ToolConfig from the test table.
* fix(providers): stop reporting an absent Ollama as an error Ollama is optional and its URL falls back to a loopback default, so a deployment that runs none refuses the probe on every poll — 10,068 of these in 14 days, the single largest error stream in the app, all of them the same expected condition. Report it the way the vLLM and LiteLLM routes already report an unconfigured base URL, and skip the probe entirely on the hosted platform, which has no local runtime to reach. An explicit OLLAMA_URL is still honoured everywhere, so a self-hosted deployment behaves exactly as before — including the localhost default that requires no configuration. * fix(providers): keep an unreadable Ollama response out of the not-reachable path The single catch covered the connection, the JSON read, and the schema parse, so a server that answered but answered wrongly was filed as 'no Ollama here'. Scope the quiet path to the connection itself and report an unusable response as the fault it is.
* credentials continue * fixes
…confirm modals (#6384) * improvement(admin): move user row actions into an overflow menu with confirm modals * fix(admin): surface password reset status outside the actions menu * fix(admin): surface ban and role change errors inside the confirm modal * fix(admin): reset the ban mutation when opening the confirm modal * improvement(admin): simplify the user row actions after review passes * fix(admin): show password reset progress while the request is in flight * fix(admin): confirm the role change the admin chose, not the live row's inverse * improvement(admin): align the role confirm and reset feedback with house patterns
* fix(chunkers): preserve FAQ prose in docs chunks
cleanContent deleted every FAQ section from the embedding index: the
multiline tag strip swallows an entire <FAQ items={[...]}/> block (it
matches from <FAQ to the first ">", often inside an answer string), and
the brace strip eats any surviving { question, answer } items — 637
Q&As across the docs never reached search, with mangled JSX fragments
embedded in their place. Consume FAQ blocks whole before the tag strip
and emit their question/answer text as plain prose, escape-aware so
braces and quotes inside answers survive. Tag and brace stripping are
otherwise unchanged — a corpus survey showed FAQ props are the only
place real page prose lives inside JSX syntax on searchable pages.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(chunkers): accept single-quoted FAQ items with trailing commas
session-policies.mdx and verified-domains.mdx write FAQ items with
single-quoted multiline values and trailing commas; the double-quote-only
item pattern matched nothing there, so the component consumer replaced
those whole FAQ blocks with a space. Capture either quote style
escape-aware (quotes of the other style inside a value are fine) and
allow the trailing comma; captured values keep their quotes and are
unquoted before unescaping.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* style(chunkers): wrap the FAQ replace call for biome
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(files): reserve embedded-image space on direct file-view loads An embedded image in a markdown file reshifted on every open: it loaded ~2.3s in with no reserved box and shoved everything below it down (CLS ~0.17). The intrinsic dimensions ARE stored server-side, but the image node view never read them at render. useWorkspaceImageDimensionsAdapter read the active files list via queryClient.getQueryData — non-reactively — so on a cold file-view load it returned null at first render and, because the adapter identity was stable, never re-checked when the list later resolved. Read the list via a reactive useWorkspaceFiles subscription instead: the adapter re-runs the image node view's memoized dimension read when the list resolves, so it reserves the box from the stored dimensions before the (slower) image download finishes. The query key is shared, so it dedupes with surrounding views. Gate it behind `enabled` (driven by the absence of a caller-supplied contentSource) so the public share page — which passes a share token as workspaceId — doesn't fire a 404. Verified in a CLS harness: dims present -> 0 shift; dims absent -> 0.20. * fix(files): stop the duplicate 'mention' extension-name warning The @-mention menu extension and the mention node were both named 'mention', so TipTap logged "Duplicate extension names found: ['mention']" on every editor (twice under the collaborative placeholder + live pair). Rename the menu extension to 'mentionMenu' (the node keeps 'mention', its persisted doc-node type) and move its editor.storage.mention -> editor.storage.mentionMenu.
* fix(secrets): preserve raw outputs with durable provenance * improvement(provenance): cleanup boundary * fix copy resources * fix fork copies to work with provenance * address comments * fix
* feat(dynatrace): add the Dynatrace integration
Adds a Dynatrace block backed by 22 Environment API v2 tools, covering the
surfaces an observability workflow actually reaches for:
- Problems: list, get, close, list comments, add comment
- Metrics: query data points, list and get descriptors, ingest line protocol
- Entities: list, get, list entity types
- Events: list, get, ingest
- Logs: search, ingest
- SLOs: list, get
- Application Security: list and get security problems
- Audit log: read
Every request path, query parameter, and response mapping is taken from the
published Dynatrace API reference — no inferred fields. Auth is an access
token sent as `Authorization: Api-Token ...` against a user-supplied
environment URL, so SaaS, Managed, and environment ActiveGate all work.
Two details worth knowing:
`ingest_event` exposes Dynatrace's event timeout as `eventTimeout`, not
`timeout`. The tool transport reserves `params.timeout` for the HTTP request
deadline, so the obvious name would have silently retargeted the wrong knob.
`get_metric` encodes its path segment with `encodeDynatracePathSegment`
rather than `encodeURIComponent`, which leaves the `:` separators in metric
keys and transformation operators intact, matching the docs' own examples.
* fix(dynatrace): close the gaps a validation pass turned up
Three real defects and one usability gap, all found by auditing the tools
against the Dynatrace API reference a second time.
`ingest_logs` double-encoded its payload. `logs` is a `json` param, and a
`json` param arrives as a *string* whenever it comes from a long-input field
or an LLM tool call — only a block-to-block reference hands over a parsed
value. `JSON.stringify` on that string produced `"[{...}]"`, so Dynatrace
received a quoted string where it expected an array. The block hid this in
the UI path by pre-parsing, but the parse lived in `tools.config.params` and
*threw* on malformed input, and it never covered the direct tool-call path at
all. Both tools now normalize through the shared `parseJsonParam`, so the
tool is correct regardless of who calls it, and the block just forwards the
raw value. `ingest_event.properties` had the identical bug.
Path identifiers were not trimmed. A problem or entity ID pasted with a
trailing newline became `%0A` in the URL and 404'd with nothing to suggest
whitespace was the cause.
Errors dropped the part that matters. Dynatrace's ErrorEnvelope carries
`constraintViolations[]`, which names the offending selector or parameter;
the generic `nested-error-object` extractor returns only `error.message`
("Constraints violated."), and which extractor won was left to fallback
order. Adds a `dynatrace-errors` extractor that folds the violations into the
message and pins it on all 22 tools. It sits after `nested-error-object` in
the chain, which already matches this shape, so no other service's error
handling changes.
Adds 21 tests covering URL construction for SaaS/Managed/ActiveGate, cursor
pagination dropping sibling filters, identifier trimming, metric-key colon
preservation, both JSON-param paths, the `eventTimeout` -> `timeout` mapping,
EntityStub flattening, the audit log's dotted `dt.settings.*` keys, and the
204/200 split on log ingestion.
* docs(dynatrace): add the page intro, and pin every response key in tests
Adds a MANUAL-CONTENT:intro block to the generated integration page covering
what the block reaches, how to get an environment URL and a scoped token for
SaaS vs Managed, how selectors work, and how cursor pagination behaves.
Verified it survives `generate-docs.ts` byte-identically.
Also closes the last silent-failure gap the validation pass left open. A
wrong top-level response key does not throw — it maps to an empty array and
reads as "no results", which is indistinguishable from a genuinely empty
environment. Dynatrace is unusually easy to get wrong here: the SLO list
returns `slo` (singular) and the metric query returns `result` (singular).
Adds a table-driven test asserting the documented key for all ten list
endpoints plus the scalar keys of the ingest and single-entity responses.
Confirmed it bites by flipping `data.slo` to `data.slos` and watching only
that row fail.
* chore(dynatrace): type the shared param map as unknown
Review follow-up. `Record<string, any>` in the block's params builder dropped
compile-time checking from every operation's shared params; `unknown` is
enough here since the values flow straight into the tool param maps. Matches
.claude/rules/sim-typescript.md, which sibling blocks (Datadog, Grafana)
still violate.
* fix(dynatrace): stop three silent failures found in a final read-through
All three turn a failed call into something that looks like a successful
empty one, which is the worst shape for an observability integration — you
cannot tell "nothing is wrong" from "the call did not work".
`readJsonBody` swallowed any unparseable body and returned `{}`. A gateway
HTML page, a captive-portal interstitial, or a truncated payload therefore
mapped every field to null and read as "no problems found". Only genuinely
empty bodies are tolerated now (201 from add-comment, 204 from log ingest);
anything else that will not parse raises with a truncated preview.
`ingest_logs` sent `[]` when the payload was missing or empty. Dynatrace
answers 204 to that, so the tool reported `accepted: true` for a call that
shipped no logs. It now fails loudly instead.
`encodeDynatracePathSegment` percent-encoded the whole metric key and then
regex-unescaped `%3A` back to `:`. Same output, but it undoes the encoder's
work and hides the intent. Colons are structural in a metric key, so it now
splits on them, encodes each part, and rejoins — which says that directly.
Each fix has a test, and each test was confirmed to fail in isolation with
only its own fix reverted.
* feat(dynatrace): add the write and configuration surfaces Takes the block from 22 operations to 47. The original PR shipped the read paths plus a few ingests; this closes the gaps that made those reads dead-end. The one that was a real defect: security was read-only. The audit-vulnerabilities skill promised "a remediation queue" and then gave you no way to act on it, even though muting is the single most common triage action. Adds mute and unmute, singly and in bulk, plus the remediation items behind a third-party finding, plus the Attacks API so an exploited vulnerability can be traced to the request that exploited it. The rest, by how much they unblock: - Custom tags (read/add/delete). Entity tags already drive every selector in the block; being able to write them closes a loop that was half open. - Settings objects (schemas, list, get, create, update, delete). This is how maintenance windows, alerting profiles, and management zones are configured in modern Dynatrace, so "open a maintenance window before the deploy" was simply unreachable before. The value is a schema-defined blob, so the tool is honestly opaque rather than falsely typed; the docs tell you to mirror an existing object. Update and delete carry the updateToken so a concurrent change fails instead of being overwritten. - Synthetic monitors and on-demand batch execution, which pairs with the deploy-marker tool to gate a release on a smoke test. - Problem comment get/update/delete, and SLO create/update/delete, completing CRUD that was previously half-built. Two structural notes. Synthetic monitors are the only endpoints still on Environment API v1, so `buildDynatraceUrl` grew a v1 sibling and the shared base-URL normalizer now strips either version; the query builder also learned to repeat a param per value, which Synthetic's `tag` needs. And creating an SLO returns 201 with an empty body and the new ID in the Location header, so that tool reads the header rather than parsing nothing. Deliberately excluded: the Grail/DQL query API. It is the long-term successor to the deprecated logs/search endpoint, but it authenticates with a platform token rather than an Api-Token, so it is a second auth path and belongs in its own change. * fix(dynatrace): drill the documented JSON shapes, and require the tag selector Two problems, one found in review and one worth more than it was given. The tag operations could run without an entity selector. All three tag tools declare `entitySelector` required, but the shared block field was only marked required for List Entities, so the block let a workflow reach those tools with an invalid configuration and let Dynatrace do the rejecting. My own structural auditor missed it because it only checked that *some* visible subBlock existed for a required param, not that the specific one was required — that check is now precise, and it confirms these three were the only instances across all 47 operations. The larger one: outputs were declaring `type: 'json'` for shapes the API reference documents in full. Thirty-five of them. The top-level entities were mapped properly, but nested payloads — a problem's evidence and impact analysis, a vulnerability's risk assessment and global counts, an attack's attacker, request, entry point and exploited vulnerability, a remediation item's assessment and mute state, the synthetic execution and failure records, the metric ingest error envelope, the DQL translation — were passed through as anonymous blobs. A downstream block could not reference `attacker.sourceIp` without knowing to guess it. All of those now carry their fields. What stays opaque is now only what genuinely is, and each says why in its description: a settings object's schema-defined value, an entity's type-dependent property bag and relationship keys, caller-supplied synthetic metadata, an audit log's JSON patch, the undocumented partial-success body of log ingestion, and the handful of security-detail shapes the reference names without expanding. * fix(dynatrace): make the synthetic enabled filter tri-state Review catch. `enabled` on List Synthetic Monitors is a three-way filter — enabled, disabled, or either — and I had it as a switch. The URL builder deliberately serializes `false` (there is a test pinning that `evaluate=false` survives), so leaving "Enabled Only" unchecked sent `enabled=false` and returned only the disabled monitors: exactly backwards. Made it a dropdown with Any / Enabled only / Disabled only, matching the monitorType field directly above it, which had the same shape and already used an empty-id "Any" option. The params mapper sends nothing for "Any". Checked the other nine switches rather than assuming. None share the bug: for each of them off genuinely means false, and false is Dynatrace's own default, so serializing it is correct. A test now pins that list so the trap cannot be re-introduced by converting one of them, alongside a test covering all three states of the filter.
…6397) * feat(workspaces): pin workspaces and widen the switcher to six rows Show up to six workspaces in the switcher instead of three, keeping the search input from six onward so it appears exactly when the list fills. Pin workspaces to the top of the switcher via the existing row context menu. Pins are per-user and global, so they live on the user's settings row rather than in `pinned_item`, which scopes every row to one workspace. They ride along on the /api/workspaces payload the switcher already loads, so the server prefetch hydrates them and pinned-first ordering never re-sorts after hydration. Drop the seat/workspace-migration disclosure copy from both invitation accept surfaces. The accept-time disclosure tokens are unchanged, so the server still verifies the outcome hasn't shifted since the page loaded. * fix(workspaces): serialize pin writes so a rapid toggle cannot be undone Each write carries the whole pin list, so two overlapping requests that the network delivered out of order left the earlier click as the stored state. Chain them instead, and hold reconciliation until the last queued write settles — refetching between two writes rendered the server's intermediate state and bounced the row out of the pinned group and back. * refactor(workspaces): store workspace pins in pinned_item, not user settings Workspace pins were a jsonb array on the settings row, replaced wholesale on every toggle. That shape is what forced the write serialization in 53ee94f: two overlapping toggles each sent the entire list, so the one that landed last won regardless of which the user clicked last. pinned_item is the canonical pinning table and its resource_type is plain text precisely so kinds can be added without a migration, so `workspace` joins it as a sixth kind. A pin is now one row: pinning inserts, unpinning deletes, and two toggles touch different rows and cannot overwrite each other. The serialization, the outstanding-write counter, the settings column, and its migration all go away, and deleting a workspace now cascades its pins. Reads stay on the /api/workspaces payload — the switcher needs the pins *of* every workspace, not the pins *inside* one — so the sidebar prefetch still hydrates them and pinned-first ordering is correct on first paint. * fix(workspaces): serialize same-workspace pin toggles and tolerate replays Splitting pins into rows removed the lost-update race between *different* workspaces but not the one on a single row: pin then unpin the same workspace and the DELETE could overtake its INSERT, delete nothing, and leave the workspace pinned. A mutation scope serializes them; TanStack runs onMutate before the scope gate, so the optimistic update is still immediate. Both duplicate-click replays now resolve to their end state rather than erroring — a repeat pin answers 409, a repeat unpin 404, and each means the row is already how the caller wants it. Rollback undoes its own toggle instead of restoring a snapshot, so a sibling toggle's optimistic state survives. Also: cap the switcher to the height Radix measured, since six rows can push the footer actions off a short viewport with nothing able to scroll to them; drop a dead pinned-item invalidation and a redundant ref; return the pin set from the hook to match usePinnedIds; and exclude workspace pins from the unscoped pinned-items listing, where they would read as a resource inside themselves. * fix(workspaces): hold pin reconciliation until nothing is still queued The mutation scope serializes the writes, so an earlier toggle settles while a later one is still waiting its turn. Invalidating there refetched the server's intermediate state and bounced the row out of the pinned group and back before the last write had even left the client. * fix(workspaces): count outstanding pin toggles off the mutation cache `hooks/queries/workspace.ts` has no 'use client' directive because server code imports `workspaceKeys` during SSR, so the `useRef` counter added in 3dc924d broke the production build — caught by CI, not by typecheck or tests. `isMutating` answers the same question without a hook: `onSettled` runs before the mutation leaves `pending`, so it counts itself, and anything above one means a later toggle is still queued behind the scope.
…eedback modal (#6400) #6241 deleted the --font-weight-* scale from globals.css and its fontWeight mapping from tailwind.config.ts. font-medium jumped 440/480 -> 500, font-semibold 500/550 -> 600, and body dropped 420 -> 400, so every existing call site snapped a full step above a body that got lighter. #6291 fixed packages/emcn only; the product call sites were left behind. Strips the weight class from body, label, row, and heading text across app/workspace, ee, workflow-renderer, the non-workspace route groups, and components/ui/button.tsx, whose buttonVariants injected font-medium into every consumer. Keeps it only where it steps up: markdown/prose bold and micro avatar initials. Also aligns the workflow-tree folder chevron to the sidebar section header (14px, 150ms), and on the feedback modal drops the prompt line above the Feedback field and re-homes Copy ID as a footer secondary action.
* fix(agent): overly broad check for secrets protection * remove opaque input processing * fix * address comments * fix
The pin sat inline before an always-reserved 18px options button, so a pinned row's name lost ~18px of truncation budget — pinning visibly re-truncated the name at the moment of the click, and hovering showed pin and options together. Match the chat rows: one fixed 18px slot with both absolutely positioned, the pin fading out as the button fades in. The trailing width is now constant, so pinning cannot reflow the name. The options glyph moves to --text-icon, the canonical icon token its new sibling already uses.
…6403) * fix(mship): return the chat connect flow to the tab that started it Connecting an integration from a chat credential chip opened OAuth in a new tab and returned there, so the user landed on a second copy of the app while the conversation they started from sat stale behind it. The flow now runs in a popup and returns through a new self-closing page at /oauth/chat-complete, which publishes its verdict to the shared attempt record and closes. The chat tab picks that up over its storage listener and updates in place, so it never navigates. A blocked popup takes the same route in a new tab and still lands on the completion page, so both paths share one verdict source. That verdict is now the server's: reaching the completion page means Better Auth routed the flow to its success callback. The previous check diffed the workspace credential list, which reported failure whenever a user re-authorized an account they had already linked -- that path updates the account row and creates no new credential. The lock is stricter than the label. A failure to create the credential from its draft is swallowed server-side, so a flow can report success with nothing in the workspace; the row stays retryable unless the credential actually appears. Also: the popup is named per attempt so sibling rows cannot renavigate each other's window; a cross-origin connect URL keeps the anchor's noopener instead of taking the popup path; the focus verifier reads the attempt after its refetch so a verdict published mid-flight is not overwritten; and the verifier treats a popup parked on a terminal page (/oauth-error, the workspace error exit) as finished rather than waiting on it forever. * fix(mship): settle the connect row from the popup, not from focus alone Addresses the review findings on the chat OAuth return leg. - Watch the popup on an interval. A provider interstitial bouncing to the workspace root, a denied consent on /oauth-error, or a closed window all end the flow without publishing a verdict or firing any event in this tab, so the row waited forever. The focus handler also no longer consumes the away flag when it defers to a live popup. - Focus an already-running popup on a repeat click instead of starting a rival attempt, which orphaned the first flow's verdict on an attempt id the row had stopped reading. - Settle from the refetched credentials on the popup success path, so the row's lock is corroborated and a connected row stops being clickable. Extracts the shared refetch-then-decide step into settleFromCredentials, used by the focus handler, the popup watcher, and the success path. * fix(mship): never read a disowned popup handle as a finished flow A provider page with COOP same-origin disowns the popup, and the disowned handle reports closed for a consent screen still running. The watcher took that as an ending and published 'failed' against a live flow. - Replace the boolean with a three-state observation. Only a same-origin terminal page counts as 'ended'; a closed-or-disowned handle is 'unobservable' and publishes no verdict. Closing a popup hands focus back to this tab anyway, so the focus verification settles that case. - Stop the interval before settling. The refetch leaves the status pending for its duration, so a running interval could fire again and resolve an attempt a retry had since replaced. - Gate the success toast on a launched-attempt ref rather than the window handle, which the watcher clears before React applies the verdict. * fix(oauth): keep the chat connect return leg in its opener's browsing context /oauth/chat-complete runs as a popup but fell into the strict COOP rule, so same-origin moved it into its own browsing-context group the moment it loaded — disowning it from the tab that opened it. That is the documented cause of a popup that is not reliably script-closable and whose opener sees window.closed report true for a live window. Matches it to its opener's same-origin-allow-popups instead, which is the directive the platform provides for exactly this case. * fix(oauth): keep every page an OAuth popup lands on observable to its opener The popup watcher settles on a same-origin terminal page, but both entries in OAUTH_POPUP_TERMINAL_PATHS were served strict same-origin COOP, which moves the popup into its own browsing-context group. The opener could then neither read its location nor trust window.closed, so the terminal-page branch could never fire in production and a flow exiting through one of those pages left the row waiting until the user happened to refocus the tab. Serves /oauth-error and the /workspace root the same same-origin-allow-popups their opener uses. The workspace root previously fell under the strict rule while every /workspace/... route already got the permissive one. * test(mship): cover the announcement surviving an early popup release The success toast is gated on the launched-attempt ref rather than the window handle; nothing pinned that. Adds the regression test, and trims the comment duplication the fix left behind. * fix(mship): bound the wait on a popup whose outcome became unobservable A closed handle and a COOP-disowned one are indistinguishable, so the watcher published no verdict for either and relied on the focus verification to settle it. That recovers the normal case — closing a popup hands focus back — but not one where the opener was never blurred, leaving the row waiting indefinitely. Arms the same safety timeout the MCP OAuth popup uses for the same reason: past it, the row decides from the credential list rather than waiting on a verdict that is never going to arrive. Cleared as soon as a real verdict lands. * fix(mship): survive a remount while an attempt is still pending The unobservable deadline lived in the watcher effect's closure, so it was armed only by the mount that launched the popup. The transcript virtualizes: a row scrolled away mid-connect came back with no window handle and no blur behind it, and nothing re-armed the bound. Derives the deadline from the attempt's own requestedAt and arms it for any pending attempt, so a remount inherits the time remaining rather than restarting the clock or losing it. A demonstrably live popup still owns the flow and is left to the watcher. * fix(mship): bind a settle to its own attempt and keep the deadline armed Two races the previous rounds left behind. A settle read the attempt only after its refetch, so a retry landing during that window was resolved by a run it never triggered — failing a replacement whose popup was still going. The attempt id is now captured before the await and the verdict only lands if it still matches; the status is still re-read after, so a verdict published mid-refetch is not overwritten. The safety deadline was one-shot. A consent screen that outlived it consumed the timeout while still live, leaving nothing to catch the popup dying unobservably afterwards. It now re-checks at the poll interval instead of expiring against a live window. * chore(mship): tighten the comments on the OAuth popup flow Trims the COOP rationale in next.config.ts to the point, and condenses the longest blocks in the connect hook without dropping the reasoning a reader needs to keep the invariants. --------- Co-authored-by: Waleed Latif <walif6@gmail.com>
…itation previews (#6415) Two unrelated load-time fixes. On a hard refresh of /files the folders painted first and the files a beat later. Both are prefetched and hydrated together, so the files entry was not reaching the client. Each read went to its own route over an internal HTTP request; prefetchQuery swallows a rejection and shouldDehydrateQuery drops the errored entry, so a failure there silently shipped a page with that list missing, and the files read is the heavier of the two. Those two reads now call the data layer. Note the staging logs show no errors from that route, so this removes the failure mode without proving it was the one firing — the request it drops from the render path, and the shape fix below, stand on their own. listWorkspaceFilesWithShares is shared by the route and the prefetch and shapes its result through the route contract's response schema. listWorkspaceFiles returns contentUpdatedAt, which the schema neither declares nor passes through, so the prefetch was caching a field a client fetch never has and that vanished on the next refetch. The reads carry no authorization of their own now that they bypass the route, so the prefetch proves the viewer first. It reuses the layout's cached host-context lookup rather than re-deriving the permission, so the gate costs no extra queries. Separately, GET /api/invitations computed join previews in a serial loop and each preview issues up to three queries of its own, putting all of them on the critical path of the workspace switcher opening. Bounded with mapWithConcurrency; the mapper was already total, which is what that helper requires.
* fix(tables): keep row context menu labels on one line * improvement(emcn): ellipsize menu row labels instead of clipping them
The sidebar and its divider are fixed to the viewport, so at the end of the page the footer was drawn over them and the lower part of the nav list became unreachable. FooterOverlapProbe publishes how far the footer reaches into the viewport as `--docs-footer-overlap`. The sidebar reads it as `bottom`, so it keeps its full height and slides up out of view as the footer arrives; the divider reads it too but is shortened rather than slid, so it terminates on the footer's top border instead of stopping short. Measured against the viewport rather than the document on purpose: the value is a constant 0 while the footer is off screen, so a content-height change higher up the page cannot move the sidebar. Verified with Playwright at 1280x800 and 2000x1100 — expanding/collapsing an FAQ with the footer off screen moves the sidebar 0px/0px and leaves the content column unchanged, and at the page bottom the sidebar's bottom edge lands within ~1px of the footer's top.
…#6419) The text and table bubble menus stayed pinned to a viewport position when the file scrolled — clicking a table cell then scrolling left the toolbar floating over unrelated content. TipTap v3's BubbleMenu reposition listener defaults to `window`, but the editor scrolls inside an inner overflow container, so it never fired; the menu only moved when the selection itself changed. Pass the editor's scroll container as the BubbleMenu `scrollTarget` (a first-class TipTap option) so it repositions with the selection, and enable Floating UI's `hide` middleware so the menu hides once its anchored cell scrolls out of view. Share the anchor + options through one `floating-anchor` helper so the two menus can't drift. Removes the prior workarounds that fought this: the `strategy: 'fixed'` viewport-pin, the resolveAnchor viewport-clamp branches, and the bubble menu's selection-keyed rect cache (which froze the menu in place on scroll). Verified in a harness: on scroll the menu delta matches the cell delta (follows), and it hides once the cell leaves view.
…s auth types (#6426) * fix(knowledge): apply knowledge-base access checks consistently across auth types The tag-definitions route only ran its knowledge-base access check for browser sessions, skipping it for internal JWT callers. Authorize on the acting user for every auth type instead — read access for GET, write access for POST — and require an acting user to be present, matching the sibling knowledge routes. Thread the acting user through the KB tag schema enrichers so their request carries the identity the route now authorizes. * chore(tests): mark tag-definition test fixtures as const
…exports (#6427) Check the acting user's workspace access before a function execution uses a request-supplied workspaceId, and gate workspace file writes in the shared VFS writer so every caller is covered by default. Access is resolved once per request and threaded through the export path so the added check does not re-query per output file.
* improvement(file-parsers): bound PDF text extraction Extract page text through pdf.js's streaming API with page, character, and wall-clock budgets instead of buffering the whole document, so extraction memory stays bounded regardless of input. Release the document proxy when done, and route output through sanitizeTextForUTF8 like the other parsers. * fix(file-parsers): only flag truncation when PDF text is actually dropped
#6428) Fold the internal- and polling-provider exclusions into acceptsPathWebhookDelivery so the generic per-webhook path route has one predicate deciding which providers it serves, instead of the route body re-deriving it. Widen isPollingWebhookProvider to accept a nullable provider, matching isInternalTriggerProvider, and drop the resulting `?? ''` at both call sites. Regression coverage is sourced from the trigger registries so a newly added internal or polling trigger is covered automatically.
…6430) The download name is the user's originalName, which only rejects path separators, so a quote reached the quoted filename parameter unescaped and could close it and append parameters of its own. An injected filename* is the one that matters: RFC 6266 tells clients to prefer it, so it decides the name the file lands under on disk regardless of what the UI showed. - neutralize the quote, backslash and non-printable characters in the quoted parameter, and neutralize the semicolon there too since that fallback exists for clients liable to split parameters without honouring the quoting - percent-encode the filename* ext-value fully, including the characters encodeURIComponent leaves raw — the apostrophe is the ext-value delimiter - names that are already safe printable ASCII keep their exact previous header Also stops a control character in a name from producing an invalid header value, which previously made the download 500.
* feat(mintlify): add Mintlify integration Adds all 18 documented Mintlify REST API endpoints across deployment, automation, agent jobs, prose detection, docs search/assistant, and analytics export. * refactor(mintlify): tighten payload typing and test import
* feat(integrations): add Snowflake PAT integration * fix(snowflake): scope block params by operation * refactor(integrations): simplify Snowflake safeguards * refactor(snowflake): isolate statement capabilities * fix(snowflake): localize required user agent * chore(snowflake): limit changes to integration scope * fix(snowflake): correct SQL generation, transport, and param conventions Address defects found by validation against the Snowflake SQL API v2 and SQL reference docs. SQL generation: - lift PARSE_JSON out of the VALUES clause into a projecting SELECT; the previous form is rejected for any object or array value - escape backslashes as well as quotes in string literals, closing a COPY option injection through the user-or-llm stagePath and pattern fields - reject "--" in stage paths, which commented out every following clause - emit COPY INTO clauses in the documented positional order - exclude only view types in introspect_schema so temporary, external, and event tables are visible - use plain equality in MERGE and reject null or duplicate match keys - bound rows and bound-value bytes for every statement, measured in UTF-8 - reject qualified task names, which TASK_HISTORY silently ignores - replace a raw NUL byte in the source with its escape sequence Transport: - read DML stats from the documented top-level ResultSet property - drop Link-header and 391908 paging, which belong to the retired API, and report partition completeness as unknown rather than falsely complete - require a 2xx status before trusting a success SQLSTATE - cap response bodies and fail closed on invalid session context names Conventions: - inline shared params into each tool instead of cross-file spreads, which also lets the docs generator emit host and apiKey - use the official Snowflake brand mark on a white tile * fix(snowflake): emit task history time bounds as literals TASK_HISTORY only accepts bind variables for RESULT_LIMIT and TASK_NAME per BCR-1410, and that change explicitly excludes a bind passed through another function first. A bind in SCHEDULED_TIME_RANGE_START/END is therefore dropped without an error, so the requested window became a no-op and the function fell back to returning the most recent runs. Emit validated literals instead, which also restores Snowflake's seven-day range error. Also reject a fractional skip-file percentage at the block boundary rather than in the builder, and correct the cancel description: a cancelled child marks the task graph run failed, so downstream tasks are skipped rather than continuing. --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Mac.localdomain> Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> Co-authored-by: Waleed Latif <walif6@gmail.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Too many files changed for review (815 files, 500 file limit). |
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 35897102 | Triggered | Generic High Entropy Secret | 0aae736 | apps/sim/executor/utils/resolved-secret-matcher.test.ts | View secret |
| 35897103 | Triggered | Generic Password | 0aae736 | apps/sim/executor/utils/resolved-secret-match-policy.test.ts | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
PR SummaryHigh Risk Overview Integrations & docs add large Dynatrace and Mintlify surfaces (tools, blocks, docs MDX) plus Snowflake; docs pick up vendor icons and integration nav entries. Provenance guidance (skills/commands/validate-integration) replaces the centralized Desktop Mothership OAuth now carries Docs site pins the sidebar with UI conventions add list/menu ordering rules (mirror sidebar/toolbar via shared constants like Security & reliability (from the release notes in this train) include workspace-scoped file export permissions, bounded PDF/HTML/OOXML/HEIF parsing, webhook path-delivery centralization, MCP/API key policy alignment, AgentMail webhook fixes, CLI per-install secrets, knowledge-base access consistency, inbox disable atomicity, and copilot image decode bounds. CI: CodeQL scheduled weekly (Monday) with cancel-in-progress on all events. Reviewed by Cursor Bugbot for commit 3096de8. Configure here. |
|
|
@cursor review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 3096de8. Configure here.
…mit (#6461) The 1000-row and 1 MB bound-value caps duplicated the shared 10MB tool request body gate on the same axis, at a lower threshold. Nothing fails between the two thresholds: Snowflake's 1 MB guidance is a recommendation about metadata retention rather than a hard limit, and statements above it still execute. The row array is already resident before the statement is built, so the caps also bounded no allocation the request body did not already bound. These were removed once before in 624b737 and should have stayed removed.
…st the detail fields the tools map (#6463) Unmute forwarded the shared muteReason dropdown's FALSE_POSITIVE default, but Dynatrace accepts exactly one unmute reason, AFFECTED. The tool-level fallback never fired because a truthy invalid reason was already supplied, so unmute failed from the block unless the reason was changed by hand. The vulnerability, problem, and attack detail endpoints omit every optional property unless it is named in `fields`, so the descriptions, remediation guidance, affected entities, root-cause evidence, and attacker details those tools map were always null.
* fix(pi): compact cloud event streams * fix(pi): select bash for event pipeline * address comments --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local> Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
…6468) ConditionBlockHandler forwarded collectBlockData's full blockData — every block output accumulated so far in the run — to function_execute on each condition evaluation. The resolver already inlines every <block.field> reference into the expression before the handler runs, so that payload was never read; it only inflated the request body. Inside a wide subflow one flat blockStates map holds every branch's outputs, so a 91-branch parallel pushed the body past the 10MB cap and failed the gate with "Request body size limit exceeded" even though the expression was just a boolean compare. Per-value large-value offload does not catch this: its threshold is 8MB for a single value, while this is an aggregate of many medium ones. Mirrors FunctionBlockHandler, which moved to blockData: {} in #4560 and left the condition handler on the old path. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore(pi): organize mode implementations * feat(pi): add plan mode * fix(pi): preserve plan exploration timeout * fix(pi): clean plan mode output * fix(pi): stream only final plan content * chore: trigger CI after retargeting * fix(pi): compact plan mode event streams --------- Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local>
…6472) Co-authored-by: Sim Pi Agent <pi@sim.ai>
… at build (#6471) * fix(og): read OG fonts from the repo instead of fetching Google Fonts at build The release build died prerendering an integration OG card with "No fonts are loaded. At least one font is required to calculate the layout." loadGoogleFont swallowed every failure and returned null, so a throttled fetch produced an empty fonts array, and Satori requires at least one. Six routes build OG images -- integrations/[slug] alone is 237 pages -- and each render fetched two weights subsetted by &text=, a per-page URL no cache can reuse. Several hundred uncacheable requests to one host from one CI egress IP across parallel build workers, so a page losing that race was expected, not unlucky. Mintlify was just whichever page drew the short straw; its description is 56 chars, unremarkable next to slack's 141. Geist 400/500 now ship in public/brand/fonts and are read once at module scope, per Next's ImageResponse guidance. .ttf because Satori accepts only ttf/otf/woff -- the .woff2 already served to browsers cannot be reused. public/ needs no outputFileTracingIncludes entry: the Dockerfile copies it into the runner, which the force-dynamic share-token card needs since it renders per request. Output is unchanged: rendering the same card with the full font and with the old subset produces a byte-identical PNG (0 of 3,024,000 subpixels differ). Render drops from ~74ms plus ~425ms of font fetching to ~74ms, and the share card no longer makes two Google round trips per request. * docs(og): record why process.cwd() is the app dir in the container Review flagged the font path as invalid in the standalone image, reasoning that the container starts at the monorepo root. It does -- but Next's generated standalone server.js opens with process.chdir(__dirname), and that file ships beside public/. Same reason content/ is read this way at runtime.
* Override * Validation improvements * remove from helm * update helm * Update Helm chart version from 1.6.0 to 1.5.2 sid wuz here --------- Co-authored-by: Waleed <walif6@gmail.com>

Uh oh!
There was an error while loading. Please reload this page.