Commit Graph
49 Commits
Author SHA1 Message Date
Waleed 746a4496ba chore(deps): upgrade next to 16.3.1, its optimizer no longer deletes live code (#6777)
* chore(deps): upgrade next to 16.3.1, its optimizer no longer deletes live code

16.3.0 was reverted in #6242 because its Turbopack optimizer modelled a bare
`return <asyncCall>()` tail call inside an async function as returning the
promise object, propagated that always-truthy fact through the caller's `await`,
and deleted everything after the resulting `if`. That shipped two dead code
paths to production: the whole `POST /api/credentials` create path, and the
insert inside `upsertAsyncToolCall`.

We reported it as vercel/next.js#96595. The fix — "[turbopack] Collapse nested
promises in the analyzer" (vercel/next.js#96601) — folds `Promise<Promise<T>>`
to `Promise<T>` in the analyzer, and was backported as #96675 and released in
16.3.1.

Verified before taking the bump:

- The minimal reproduction from the issue no longer reproduces on 16.3.1. All
  four routes keep their code; on 16.3.0 `/api/broken` lost everything after
  the `if`.
- A production build of `apps/sim` on 16.3.1 still emits the markers whose
  disappearance was the original signal: `credential_connected` (43 files),
  `acquireOrganizationUserMutationLocks` (28), and the `upsertAsyncToolCall`
  insert-path warning (10).

The `return await` hardening added to both sites in the revert stays as is, and
so does the TypeScript toolchain configuration.

16.3.1 published 2026-08-13, so it is inside the 7-day `minimumReleaseAge`
supply-chain window until 2026-08-20 and needs an exclusion to install. The
alternative is sitting on 16.2.12, whose successor we already reverted once, so
the entries go in dated and come out on the next touch of the file. The mermaid
and js-yaml exclusions aged out on 2026-08-11 and 2026-08-07 and are dropped
here per that same rule.

* fix(deps): keep the musl and win32 SWC binaries in the lockfile

The release-age exclusion only listed the four @next/swc platforms that
package.json pins, but next declares all eight as its own optionalDependencies,
so all eight are normally resolved into bun.lock. A gated optional dependency
does not fail the install — bun drops it silently — so the first install
stripped both musl variants and both win32 variants from the lockfile.

That left the Alpine devcontainer and any Windows machine with no SWC binary to
resolve. Adding the remaining four to the exclusion list restores all eight
entries at 16.3.1.

Worth knowing for the next time this happens: bun.lock is sticky here. Once an
optional dependency has been dropped, re-running the install — even with
--force, even with the age gate switched off entirely — does not bring it back,
because the resolution is not reattempted. The lockfile has to be regenerated
from a base that still contains the entries, which is why this restores
bun.lock from staging before re-applying the bump.
2026-08-17 14:04:01 -07:00
Waleed 39c3fe60d9 improvement(docs): remove Ask AI, add the missing platform surfaces, align the sidebar (#6259)
* improvement(docs): remove Ask AI, add missing surfaces, align sidebar to the app

- Removes the Ask AI widget, its /api/chat route, and the four deps exclusive
  to it (@ai-sdk/openai, @ai-sdk/react, ai, streamdown). lib/embeddings and
  docsEmbeddings stay — /api/search uses them.
- Adds --surface-7 and --surface-hover, the last two platform surfaces docs
  lacked.
- Aligns the sidebar with the app's canonical nav chrome: px-2.5 -> px-2,
  text-small -> text-sm, hover --surface-3 -> --surface-active, and an active
  hover of --surface-6, matching the Chip the app's sidebar items are built on.

* fix(docs): make the sidebar hover CSS agree with the utilities

Review caught that the sidebar hover alignment in this PR had no visual
effect. `global.css` carries !important rules for both the link and button
sidebar items — they exist to beat fumadocs' own styles — and they were still
forcing the pre-alignment values: --surface-3 on an inactive hover, and
--surface-active on an active hover.

So the Tailwind utilities were dead on arrival. The global rules now carry the
app's values instead (--surface-active inactive, --surface-6 active), matching
the utilities rather than fighting them, with a comment noting the two must
move together.
2026-08-04 12:45:10 -07:00
Emir KarabegandWaleed Latif 9b9da81a27 improvement(platform): drop lucide-react for the in-house icon set, flatten the type and border scales, and retire scheduled tasks and workflow references (#6241)
* border styling

* improvement(platform): migrate off lucide-react, flatten the font-weight scale, and retire scheduled tasks and workflow references

* chore(platform): drop the dead schedule client layer and repair stale rule and skill docs

Follow-up cleanup for the platform commit, which removed the workspace
scheduled-tasks surface and migrated off lucide-react. Both left dead tails
that type-check clean, so nothing flagged them.

Six mutation hooks in hooks/queries/schedules.ts lost their only consumer when
the scheduled-tasks page was deleted: useDisableSchedule, useResumeSchedule,
useDeleteSchedule, useExcludeOccurrence, useUpdateSchedule, useCreateSchedule.
They are removed along with the three contract objects that served only them —
disableScheduleContract, excludeOccurrenceContract, deleteScheduleContract.

disableScheduleBodySchema and excludeOccurrenceBodySchema are deliberately
kept: both are members of scheduleUpdateSchema, the discriminated union the
live PUT /api/schedules/[id] route parses. Dropping them would collapse the
union and 400 the disable and exclude_occurrence actions.

The schedule-calendar tree and its utils stay unmounted for later reuse. Its
TSDoc now says so, since it has no importer and would otherwise read as dead
code on the next sweep.

The add-enrichment skill templated an import from lucide-react, a dependency
the platform commit deleted, so running it produced an unresolvable import. It
now points at @sim/emcn/icons, matching all five shipped enrichments. The
emcn-design-review skill and several rule files still pointed at
apps/sim/components/emcn/**, which moved to packages/emcn/**.

Also corrects the documented Chip variant list — it advertised a ghost variant
that never existed and omitted border — repoints the sim-url-state date-parser
example at an inline snippet now that its source file is gone, and normalizes
the one strokeWidth the icon migration left at 1.5 in bubble-chat-delay.

* fix(platform): mark the resource chrome as client components

`skills/page.tsx` is a Server Component, and this branch moved its
`IntegrationTabsHeader` import onto the `@/app/workspace/[workspaceId]/components`
barrel. That barrel re-exports `SortDropdown` from `resource-options`, which
calls `useState`, so the server graph now reaches a client-only module and
`next build` fails. `resource-header` has the same latent problem (`useState`,
`useEffect`, `useRef`).

Both files are genuinely client components, so they get the directive rather
than the page dropping the barrel import — local feature barrels are the
convention here.

Also drops a stale `lucide-react` mention now that the dependency is gone.

* chore(scheduled-tasks): remove the scheduled-task logic

Scheduled tasks are retired. This removes the `sourceType = 'job'` half of
`workflow_schedule` from the application, leaving the workflow Schedule
trigger (`sourceType = 'workflow'`) untouched.

Gone:
- the job orchestration layer (`lib/workflows/schedules/orchestration.ts`)
  and the agent-job runner in `background/schedule-execution.ts`
- the job claim/dispatch half of the schedules execute tick
- POST /api/schedules (job creation) and the job branches of
  GET /api/schedules and PUT/DELETE /api/schedules/[id]
- the copilot job tools and handlers, the `scheduledtask` resource type and
  chat-context kind, and the VFS `jobs/` materialization
- the scheduled-task analytics events and the job variant of the
  schedule-disabled email

Kept on purpose: `scheduled-tasks/components/schedule-calendar/**` and
`scheduled-tasks/utils/**`, which the agents module will reuse.

`packages/db/schema.ts` is deliberately untouched — the columns stay for now
and come out in a follow-up with a proper expand/contract migration.

The generated copilot catalog and VFS snapshot types are regenerated from
the matching copilot PR, which removes the tools and the `jobs` snapshot
field at the source.

Verified: 23/23 type-check, biome, api-validation, production build, and the
full vitest suite (18361 passing; the one failure in
executor/handlers/pi/cloud-review-tools.test.ts predates this branch).

* fix(sidebar): derive the settings and switcher widths from SIDEBAR_WIDTH

This branch moved `SIDEBAR_WIDTH.DEFAULT` from 248 to 238 but left two
hardcoded `248px` chrome widths behind, so both sat 10px wider than the live
sidebar:

- the workspace-switcher menu, which is meant to line up with the sidebar
  column it drops out of
- the standalone settings sidebar, whose own comment says to keep it in step
  with the in-workspace chrome

Both now read `SIDEBAR_WIDTH.DEFAULT` directly rather than repeating the
number, so the next change to the constant cannot leave them stale again.

* fix(schedules): stop the API accepting actions it no longer handles

Adversarial pass on the scheduled-task removal found a real regression in
PUT /api/schedules/[id].

Removing the job-only `update` and `exclude_occurrence` handlers left them in
`scheduleUpdateSchema`, so those bodies still parsed. The handler chain is
`disable` first and then an unguarded fall-through to reactivate, so an
`action: 'update'` request would have silently REACTIVATED the schedule
instead of being rejected.

Both actions are dropped from the discriminated union, so `parseRequest` now
rejects them with a 400. Their bodies, response types and the orphaned
`createScheduleContract` (its POST route is gone, and nothing imported it)
go with them.

* chore(landing): retire the scheduled-tasks marketing surface

The feature is gone from the product, so the marketing pages stop selling it.

- deletes the `/scheduled-tasks` landing page and its calendar-loop hero, and
  the `LandingPreviewScheduledTasks` panel
- drops the view from the landing preview: the `SidebarView` member, the nav
  entry and its now-unused Calendar icon, the callout label, both render
  branches, and the staged chat copy in `workflow-data`
- removes the navbar and footer links and the sitemap entry
- removes the route from `LANDING_ROUTES`, the COEP exemption list that must
  list every `app/(landing)` route

`/scheduled-tasks` is indexed, so it 301s to `/workflows` rather than starting
to 404 — that is the surface that still carries scheduled execution via the
workflow Schedule trigger.

Left alone deliberately: `demo-scheduler` is the Cal.com booking embed for the
demo page, unrelated to this feature, and the scheduling library article is a
generic SEO piece that never pitched it.

* perf(chat): stop the resource picker fetching schedules it no longer shows

Dropping the `scheduledtask` group from the add-resource dropdown left
`useWorkspaceSchedules` behind, so the picker still issued a workspace
schedules request whose result never reached a group.

Worse than a wasted request: `schedulesPending` was still in the hydration
gate, so the whole picker waited on that response before it could settle, and
`schedules` was still a `useMemo` dependency, re-running the group build when
it resolved.

The hook and its route stay — `/api/schedules?workspaceId=` still correctly
lists workflow schedules, unlike `createScheduleContract`, whose route this
branch removed.

* chore(scheduled-tasks): drop the leftovers the removal stranded

An independent audit of the branch turned up dead code and stale docs that the
compiler cannot see — nothing behavioural, but all of it rots silently.

- README still sold the feature: the "Scheduled tasks" tile, the prose listing
  it as a workspace surface, and the now-unreferenced screenshot. The landing
  surface went in c61770a8c; this tile was missed.
- `resource-content.tsx`: `SCHEDULE_STATUS_LABEL`, `formatScheduleInstant` and
  `ScheduledTaskField` were orphaned when the schedule render branch went.
- `computeNextRunAt`: zero callers, including tests — its only consumer was the
  removed agent-job runner.
- `applyScheduleUpdate`'s `allowCompleted` option: no call site passes it, and
  its comment described self-completion, which no longer exists. The guard stays
  (legacy `sourceType='job'` rows still carry `status='completed'` until the DB
  follow-up); it is simply unconditional now.
- Three TSDoc blocks still described a create-job route and "opening a
  scheduled-task artifact".

Type-check re-run with --force, since a cached turbo replay is not a check.

---------

Co-authored-by: Waleed Latif <walif6@gmail.com>
2026-08-04 10:28:00 -07:00
Waleed 2977db5133 fix(deps): revert next to 16.2.12, its 16.3.0 optimizer deletes live code (#6242)
Next 16.3.0's Turbopack optimizer models a bare `return <asyncCall>()` tail
call inside an async function as returning the promise object, then propagates
that always-truthy fact through the caller's `await`. Where the result feeds an
`if (x)` whose every branch returns, it concludes the branch is always taken and
deletes everything after it from the emitted bundle.

Two sites shipped to production that way:

- `POST /api/credentials` lost its entire create path — the transaction, the
  org locks, the insert, the audit, the 201. A first-time create fell into the
  existing-credential branch and threw on `existingCredential.id`, so every new
  credential 500'd.
- `upsertAsyncToolCall` collapsed to `async () => await getAsyncToolCall(id)`.
  The insert is simply gone; it returns null for every new async copilot tool
  call. Silent — no error, no failed request.

A differential scan of 71,266 source string literals across `.next/server` and
`.next/static`, comparing images built from the same commit on 16.2.12 and
16.3.0, found exactly these two and nothing else. That scan cannot see dropped
branches with no distinctive string literal, which is why the version goes back
rather than the two sites being patched alone.

Both are also hardened with `return await`, verified to defeat the miscompile in
a minimal reproduction. The TypeScript toolchain cleanup from the original bump
(dropping @typescript/native-preview, `useTypeScriptCli`) is kept.
2026-08-03 22:31:43 -07:00
Waleed ed17bb2bac chore(deps): upgrade next to 16.3.0 and clean up the TypeScript toolchain (#6235)
Bumps next, @next/env and the @next/swc-* optional deps to 16.3.0 across the
root overrides, apps/sim, apps/docs and packages/emcn.

Two config notes worth keeping:

- `experimental.turbopackFileSystemCacheForBuild`'s default flipped false ->
  true for stable in 16.3.0, so our explicit `false` is now load-bearing rather
  than defensive. Without it this bump would have silently re-enabled a build
  cache measured 3.2x slower on this codebase (#6078). Comment updated to say so.
- `experimental.useTypeScriptCli: true` is now pinned. TypeScript 7 ships no
  JavaScript compiler API until 7.1, so Next's default checker cannot run and
  needs the project-local `tsc` CLI instead. 16.2.12 was silently skipping
  build-time type checking entirely because it detected @typescript/native-preview
  and short-circuited the stage ("Finished TypeScript in 138ms"); pinning the flag
  keeps that from drifting back.

TypeScript toolchain cleanup that the upgrade makes possible:

- Drop @typescript/native-preview from apps/sim and apps/docs. It was the
  pre-release channel for TS 7 and is superseded by typescript@7 (nightlies now
  ship as typescript@next), and its presence is what suppressed build type checks.
- Align packages/browser-protocol and packages/terminal-protocol from
  typescript ^5.7.3 to ^7.0.2 so every workspace is on one compiler version.

apps/sim keeps @typescript/typescript6 as a production dependency: the function
sandbox at app/api/function/execute dynamically imports the TS 6 compiler API to
transpile user code, and TS 7 has no API to replace it yet.

Build and dev were benchmarked 3x per version on a byte-identical tree; the
upgrade is performance-neutral (build median 100s -> 99s, dev:full warm 10s -> 9s).
2026-08-03 18:30:11 -07:00
Waleed 28abbc94c6 perf(dev): re-enable the Turbopack dev filesystem cache (5.4x faster restarts) (#6151)
* perf(dev): re-enable the Turbopack dev filesystem cache (5.4x faster restarts)

`turbopackFileSystemCacheForDev` has been `false` since #5408 — a landing-page
homepage redesign whose description covers hero cards, feature-card aspect
ratios, eyebrow chips and a voice-input button color, and never mentions
Turbopack, caching, or dev performance. It was collateral, not a decision, and
it overrode the Next default (true since v16.1).

It is not the flag #6078/#6080 measured. That A/B was `...ForBuild` and its
conclusion stands — the build cache is a 3.2x regression and stays off. The two
flags look alike and are opposite decisions; both are now commented as such.

Measured on `/workspace/[workspaceId]/w`, n=3 per arm, SIGINT between runs:

  cache OFF   31.4s / 30.1s / 31.9s   RSS 9.0-9.8 GB
  cache ON     5.6s /  5.6s /  5.5s   RSS 4.4-5.1 GB

5.4x faster restarts, ~2x less resident memory. Cold compile against an empty
cache is unchanged (~32s either way) — the cache only pays back on restart,
which is the loop that actually hurts.

The cache is unbounded on disk: the abandoned one on this machine had reached
78 GB across 1,848 SST files, and a stale cache is slower to read back, so left
alone it erodes the win it exists to provide. `prune-turbopack-cache.ts` runs on
`predev` and drops it past a cap (default 20 GB, `SIM_TURBOPACK_CACHE_MAX_GB` to
override); `bun run dev:cache:prune` forces it. It never blocks `next dev` on a
maintenance failure.

Adds a `dev-performance` skill recording the cost model, the reference numbers,
and the benchmarking method — including that stopping the server with `kill -9`
mid-cache-write discards the cache and makes this exact win read as no win.

* improvement(dev): chain the cache prune into dev scripts instead of a predev hook

Review read the root `bun run dev` path as bypassing the `predev` hook and so
never capping the newly-enabled cache. Turbo does fire `pre*` hooks — verified
live, the run prints the prune before `next dev` — but the concern is fair in
that the guarantee rested on package-manager lifecycle semantics that are
invisible at the call site.

Chaining it explicitly removes the question entirely: every `dev` variant now
runs `bun run dev:cache:cap && …`, which holds on any invocation path, is
visible in the command itself, and drops the three duplicated `predev:*` entries
for one shared script.

Verified on both paths — direct `bun run dev` and root `turbo run dev`, the
latter printing:

  sim:dev: $ bun run dev:cache:cap && next dev --port 3000
  sim:dev: $ bun run ../../scripts/prune-turbopack-cache.ts

* docs(dev): document cache-corruption recovery, the cost of enabling the cache

Stress-tested the failure mode rather than assuming it: deliberately corrupting
an SST block makes Turbopack abort with a FATAL panic — it does not self-heal.

  FATAL: An unexpected Turbopack error occurred.
  Cache corruption detected: checksum mismatch in block 4 of 00000221.sst

`bun run dev:cache:prune` and restart fixes it; verified the canvas serves 200
again afterwards. Documented in the skill and in the script's header, since the
symptom is a hard crash and the remedy is not guessable.

This is the honest cost of turning the cache on. It is worth paying — a 5.4x
faster restart against a rare, loud, single-command failure — but it should be
written down rather than discovered.

Worth distinguishing from the adjacent case: an ordinary hard kill does *not*
corrupt the cache. Turbopack discards a partially-written cache and rebuilds it
silently, which is exactly why a `kill -9`-based benchmark reads as "no cache
win" (noted in the benchmarking section).

* refactor(dev): drop the dev-performance skill, keep its findings at the code

A whole skill was too much for what this is. The parts that are load-bearing —
why the two lookalike cache flags are opposite decisions, the measured numbers,
the corruption remedy, and the benchmarking trap — now live in the config and
script they describe, where someone changing the flag actually reads them.

The trap is the piece worth keeping: `next dev` compiles on demand so startup
time is meaningless, and stopping the server with `kill -9` makes Turbopack
discard a partially-written cache and rebuild silently — which reads as 'the
cache does nothing' and is how this flag stayed wrong for a month.

Dropped rather than relocated: generic advice that was not specific to this repo
(antivirus, Docker-on-macOS, orphaned processes) and a measured no-op
(`optimizePackageImports` for lucide-react changed nothing, 31.6s vs 31.7s).

* docs(dev): record the measured cost and concurrency behaviour of cache pruning

Stress-tested the maintenance path rather than assuming it is free.

Cost: the size walk is ~30ms on a real cache and ~85ms at 2,000 files — under 2%
of a 4.2s warm restart, and invisible against a cold one. It runs before every
dev start, so it needed to be cheap; it is.

Concurrency: pruning while a dev server is live (which happens when a second
server is started from the same checkout) does not crash it. The running server
keeps its in-memory state and kept serving HTTP 200 with zero panics. It does
stop persisting for the rest of that session, so its next start is cold once —
verified recovering at 23.4s then 4.5s. Worth writing down because the directory
silently never reappears mid-session, which looks like a bug if you go looking.

The cap is a backstop, not routine: a normal session sits at 1-2 GB against a
20 GB default.

* fix(dev): cap every app's Turbopack cache, not just apps/sim

`apps/docs` is a Next app too (`next dev --port 3001`) and overrides nothing, so
it uses the Next default where the dev filesystem cache is on. It already had an
uncapped 1.1 GB cache here, and the root `bun run dev` (`turbo run dev`) starts
it — so a teammate using the documented command was accumulating a cache nothing
would ever prune.

The script now resolves its target from the working directory instead of
hardcoding `apps/sim`, and each app chains its own cap. Per-app rather than one
sweep on purpose: a single pass would let one app's dev start delete a cache
another app is holding open, which costs that session its persistence.

Verified both: `apps/sim` and `apps/docs` each report and cap their own 1.1 GB
cache, and both dev servers start clean (`Ready in 299ms` / `229ms`, docs serving).

* refactor(dev): drop dev:cache:prune in favour of the existing dev:clean

`dev:cache:prune` duplicated `dev:clean`, which already existed in `apps/sim` and
does strictly more (`rm -rf .next/dev/cache` covers the Turbopack cache plus the
fetch and image caches). Two commands for one job is worse than one, and the
docs pointed at the newer, narrower of the two.

Removes it from both apps and gives `apps/docs` the `dev:clean` that `apps/sim`
already had, so the recovery command is the same everywhere. `dev:cache:cap`
stays — it is the chained step, used by more than one dev variant, and naming it
keeps the relative script path out of each command.

Verified `dev:clean` is a real remedy: corrupt a cache block, run it, restart —
canvas serves 200 with no panic.

Also corrects an overstatement. A damaged cache does not *always* abort
Turbopack; whether it panics depends on whether the damaged region is read, so
it is not reliably reproducible. Both notes now say "can abort" and give the same
remedy either way.
2026-08-01 11:27:35 -07:00
Waleed f0b79c5cc6 chore(deps): upgrade next 16.2.11 -> 16.2.12 (#6077)
16.2.12 is the current stable (published 2026-07-25) and its entire
changelog is two PRs: a docs backport and vercel/next.js#95831, "Fixes to
support TypeScript 7".

That second one matters here. `apps/sim` declares `typescript: ^7.0.2` and
the lockfile resolves 7.0.2, while 16.2.11 predates any TS7 handling — not
even the actionable-error guard (#95837), which was never merged. The
upstream symptom is `next build` dying with a silent SIGSEGV during its
type-check step, because the legacy TypeScript JS API that Next called is
gone in TS7. Builds pass today only because Next detects
@typescript/native-preview as the compiler and takes a different path, so
we are accidentally-working rather than supported. 16.2.12 adds the
`experimental.useTypeScriptCli` backend that makes this configuration
official.

Zero build-performance content in the patch, so this is not a speed change.

Bumps all eight pins in lockstep — next, @next/env and the four
@next/swc-* binaries at the root, plus the three app/package copies. The
swc binaries must move with next: they are platform-gated
optionalDependencies, so a version skew or a gate exclusion leaves them out
of bun.lock entirely and `bun install --frozen-lockfile` installs no
compiler at all (the #5945 failure).

Also re-dates the bunfig.toml gate note, which said to drop the next
entries on 2026-07-28 — yesterday. 16.2.12 is inside the 7-day window until
2026-08-01, so following that instruction would have blocked this bump and
re-triggered the missing-compiler failure. Re-date on future bumps rather
than deleting the entries early.
2026-07-29 18:10:50 -07:00
1d64b92b41 feat(desktop): desktop app (#5998)
* top on a desk

* fix auth stuff

* intermediate state

* update

* local filesystem fixes

* Huge

* fix banner

* ci: disable desktop release + e2e in CI for now

The desktop-release reusable-workflow call requested contents: write,
which ci.yml's permission grant (contents: read) rejects — invalidating
the whole CI workflow. Desktop is tested locally for now; signed builds
remain available manually via desktop-release.yml workflow_dispatch, and
desktop e2e via its own workflow_dispatch.

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

* ci: exempt electron from the release-age gate (time-boxed)

electron@43.1.1 (published 2026-07-14) is exact-pinned for the desktop
shell and blocked by minimumReleaseAge until 2026-07-21. Excluded with a
drop-after date, following the vetted-typescript precedent. Verified the
rest of the desktop dependency set clears the 7-day gate.

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

* desktop: brand app icon (packaged + dev Dock)

- build/icon.icns regenerated from public/logo/primary/large.png on the
  Apple icon grid (824px body, r=185.4, centered on a transparent 1024
  canvas), compiled with iconutil
- dev runs set the same mark via app.dock.setIcon (static/dock-icon.png) —
  unpackaged Electron otherwise shows its default atom icon
- un-ignore apps/desktop/build: it holds electron-builder INPUTS (icon,
  entitlements), which the /apps/**/build output rule was swallowing —
  the icns and entitlements were never actually tracked
- revert resetAdHocDarwinSignature fuse: it corrupts the packaged binary
  signature (app killed at launch on arm64); the local ad-hoc deep-sign
  flow doesn't need it

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

* desktop: switch app icon to the b&w brand mark

White rounded tile with the black sim wordmark (from
public/logo/b&w/large.png), replacing the purple variant. Same Apple
icon grid geometry (824px body, r=185.4, 1024 canvas).

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

* fix banner

* Fix

* clean up launcher

* fix oauth

* update desktop app

* Improve browser use and consolidate desktop app

* Desktop app ui cleanup

* Updates

* Updates

* remove dev tool option

* Browser updates

* Fix electron bug

* Browser shortcuts

* lifecycle

* feat(desktop): SSRF hardening + shared @sim/security/ssrf (re-home of #5763) (#5784)

* feat: re-home @sim/security/ssrf + sim SSRF dedup onto dev (clean core)

* feat(desktop): re-integrate SSRF guard + hardening onto rewritten dev

Re-applies the browser-agent SSRF guard and hardening onto dev's evolved
desktop files (dev rewrote session/driver/handoff/index and split out
errors.ts/keyboard.ts):

- session.ts: agent-partition onBeforeRequest is the SSRF choke point —
  DNS-resolving check (fail-closed) for document navigations, synchronous
  literal-IP backstop for subresources.
- driver.ts: browser_navigate/browser_open_tab validate via checkAgentUrl for a
  clean model error; also adopt shared sleep/getErrorMessage and drop the local
  reimplementations + banner separators.
- index.ts: local-only crashReporter (native minidumps, no upload) + CSP
  fallback wired into the app session.
- window.ts: record the crash-dump dir on renderer_gone.
- config.ts: drop the local LOCAL_HOSTNAMES set for the shared isLoopbackHostname
  (also removes the dead bare '::1').
- cdp.ts: per-WebContents callbacks so a background tab's events reach its own
  driver.
- updater.ts: the manual check now surfaces network/manifest failures instead of
  silently swallowing them.
- README: correct the App Sandbox / security-scoped-bookmark note.
- electron-mock: webRequest.onBeforeRequest + crashReporter stubs.
- api-validation: annotate dev's validated-envelope double-cast; bump the
  route-count baseline 964→965 for dev's already-merged route (ratchets stay
  tight; non-Zod and double-cast at baseline).

Skipped as moot (dev already did them independently): launcher isVisible removal,
decideStartRoute param drop, local-filesystem clear() removal.

* chore(desktop): biome format install-local.ts (pre-existing dev lint failure)

* refactor: apply audit cleanup (reuse + simplify)

- domain-check: drop the redundant isIpLiteral guard (isLoopbackIp already
  validates and returns false for non-literals).
- session.ts: use shared getErrorMessage instead of the local error ternary
  (the file already imports it).
- tray.ts: use shared sleep() instead of a hand-rolled setTimeout promise.
- updater.ts: distinguish the synchronous-throw log from the async-rejection
  log on the manual update check.

* refactor: /simplify pass + review fixes

- url-guard: bound the SSRF dns.lookup with a 5s deadline (fails closed on
  timeout) so a slow/hung resolver can't suspend the check and the
  onBeforeRequest callback indefinitely (Greptile P2); + test.
- Finish the reuse consolidation the earlier pass missed: session.ts second
  error ternary → getErrorMessage; the bracket-strip idiom → unwrapIpv6Brackets
  in input-validation.ts, input-validation.server.ts (×2), onepassword/utils.ts
  (fixes the check:utils banned-pattern CI failure).
- driver: document why the tool-level checkAgentUrl coexists with the
  onBeforeRequest enforcement seam (clean model error; loadURL rejection is
  swallowed).

* fix(desktop): swallow late DNS rejection after the SSRF lookup timeout (Cursor)

* refactor: split pure host helpers into @sim/security/hostnames (ipaddr-free) (#5787)

unwrapIpv6Brackets + isLoopbackHostname move to a new ipaddr-free sub-export so
client code can share them without pulling ipaddr.js into the browser bundle.
ssrf.ts re-exports both, so its server/desktop consumers are unchanged. This
eliminates the duplicate isLoopbackHostname in apps/sim/lib/core/utils/urls.ts:
urls.ts and its three client importers (mcp queries, oauth probe, oauth
url-validation) now use the single shared definition.

* Desktop app fullscreen mode

* fix(copilot): report closed browser session as a distinct terminal tool error

A dead agent browser session used to answer every browser tool with an
indistinguishable generic ~30s IPC timeout, which the model retried
indefinitely (one turn: 59 minutes of failing browser_snapshot calls).

- When the desktop app has reported the session closed, page-dependent
  browser tools fail immediately with an explicit session-closed message
  (and sessionClosed: true in the result data) instead of burning the full
  timeout per call. browser_navigate / browser_open_tab / browser_list_tabs
  still run, since they can start a new session.
- A failure whose session died mid-call (e.g. during a takeover) gets the
  same tag appended, so the model learns the terminal cause rather than
  seeing a plain timeout.

Companion to mothership's tool_failure_loop circuit breaker.

* fix(desktop): route Cmd+W to focused browser tabs

* fix(desktop): reserve macOS title bar safe area

* fix(desktop): limit title bar safe area to login

* fix install script

* feat(desktop): improve local folder settings

* feat(desktop): harden local capabilities and window chrome

* fix(invitations): live refetches

* fix(desktop): make manual update checks use updater state

* fix(desktop): review fixes — OAuth error handling, query freshness, invitations

Findings from an end-to-end review of the desktop work, fixed and verified.

OAuth connect/login handoff:
- Add a friendly /oauth-error landing page + onAPIError.errorURL so provider
  Cancel/Deny (which Better Auth redirects before the flow state is parsed)
  no longer dead-ends on a 404; re-initiating supersedes the idle loopback.
- Stop a post-consent failure from reporting success (drop the baked-in
  errorCallbackURL param that collided with Better Auth's appended code;
  coerce an array error defensively on the complete page).
- Guard the desktop connect listener with the same context-age check the web
  routers use, so an abandoned flow can't mislabel a later completion.
- Clear an orphaned pending handoff when a loopback re-bind fails.

Query freshness (desktop refetchOnWindowFocus):
- Pin refetchOnWindowFocus off on queries that seed editable forms
  (environment/secrets, credential detail, schedules) so a background focus
  refetch can't drop an unsaved draft, and on the useWorkflowStates fan-out so
  returning to a large table doesn't fire N heavy envelope fetches. All no-ops
  on web (default already false).

Invitations (in-app pending invitations):
- Map accept/decline failures to friendly copy instead of raw machine codes.
- Invalidate subscription + refresh session on accept (parity with the email
  path); reconcile the list on failure (onSettled) so dead rows drop.
- Gate the modal's query on open so it no longer fetches on every app load.

CI:
- Wrap the latest-mac.yml update-feed route in withRouteHandler and allowlist
  it as a non-boundary route (input-less, YAML) so the contract audit passes.

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

* updates

* fix(desktop): use workflow colors for environment icons

* fix(desktop): use orange for dev icon border

* fix(login): change one time token generation to GET

* improvement(desktop): reveal local folders from settings

Local-folder rows rendered their glyph at 20px inside the bordered
credential tile — chrome meant for brand and logo icons — above a static
subtitle that repeated what the section already said. The row now shows a
plain 14px folder icon and the folder name alone.

Clicking a row reveals the folder in the OS file manager through a new
reveal_mount bridge op, which resolves the opaque localfs URI to a live
grant and requires an active user gesture, matching the other grant
mutations. The absolute host path still never crosses the bridge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C54QHj4WPV777Fq2yRwkcb

* improvement(desktop): row actions menu for folder grants, larger version text

Revoke moves from an always-visible chip into the canonical RowActionsMenu,
matching the MCP server rows. The version value moves off text-caption onto
text-sm — it was rendering at the subtitle size, which also shrank the
"x -> y on restart" line that matters most.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C54QHj4WPV777Fq2yRwkcb

* feat(desktop): improve browser tab usability

* fix(desktop): thicken environment icon borders

* fix(desktop): strengthen environment icon borders

* feat(desktop): support multiple windows and harden the agent browser

Sim can now open many full windows in one process. The embedded browser is
still a single native surface, so exactly one window owns it at a time.
Ownership transfers only to the focused window: without that rule, two windows
both showing the browser reclaim it on every bounds heartbeat and re-parent the
native view back and forth roughly once a second while Sim sits in the
background, where no window is focused. A destroyed owner is now forgotten
rather than left rejecting updates from the window actually on screen, and a
closing window's release is honoured even though Electron destroys it before
emitting `closed` — previously that release was dropped and the next layout
could re-parent the browser onto a window that never asked for it.

The agent's password boundary is now enforced rather than assumed. It was
treated as settled but had four ways through: `browser_press_key` sent trusted
CDP keystrokes to whatever held focus, `clickElement` focused credential fields,
`readActiveElementState` returned a preview of any focused value, and snapshots
printed the contents of revealed password fields. Detection also used
`instanceof HTMLInputElement`, which is realm-bound and returned false for
inputs inside same-origin iframes — the nested login forms that need it most.
Detection now matches on tagName/type/autocomplete, the keystroke guard runs in
the driver where trusted CDP input is visible, and typing re-checks the real
target before inserting, since login forms advance focus between the username
and password steps.

Signing out clears the embedded browser's profile. Its cookies, cache, pinned
tabs, browsing trail, and reopen list all survived sign-out, so the next account
on the machine inherited the previous user's live sessions.

Partition hardening is keyed per session instead of a process-wide flag, which
would have left a second partition with no permission handlers, no SSRF
filtering, and no download blocking — silently, and still type-checking.

Adds the first tests for page-functions.ts, including a serialization contract
check: those functions ship to the page as String(fn), so a reference to module
scope passes every other test and fails only against a real page.

* fix(desktop): close clipboard, glob DoS, and authorization holes

Found by a full audit of the desktop app against origin/staging. Each of
these was measured or asserted rather than reasoned about.

The agent could read the user's system clipboard. `browser_press_key('Cmd+V')`
pasted it into a focused field and the next `browser_snapshot` returned it as
an ordinary `value` — snapshots redact password fields, not pasted content, and
clipboards routinely hold a password copied out of a manager. The credential
guard added earlier did not catch it: `insertedTextFor` returns undefined
whenever `meta` is set, so `Cmd+V` was classified as not text-inserting.
`Control+V` reached the same place because the macOS normalizer rewrites it.
Clipboard combos are now refused before dispatch rather than by withholding the
CDP `commands` array, since off macOS these are Blink-native and a key event
alone still performs them. Copy and cut go too — they clobber the user's
clipboard as a side effect.

A glob pattern could freeze the whole app. Micromatch compiles to a
backtracking regex whose cost is exponential in wildcard count: measured
against a single 46-character path with the options this code passes, ten
wildcards took 2.7s and twelve took 43s, once per scanned entry, in one
synchronous call that the surrounding abort checks never get to interrupt. That
is the main process, so every window, the menu bar and the tray freeze with
Force Quit as the only recourse, and the pattern is model-supplied. `safeRegex`
reports the generated source as safe, so it was no defense. Patterns are now
bounded at six wildcards, which keeps the worst case near 2ms while leaving
headroom over real patterns (which top out around four). A timing probe backs
it up, with a budget loose enough that JIT warmth and machine load cannot make
it fire on a legitimate pattern — a tight budget proved flaky in both
directions.

The grep authorization guard compared `request.pattern !== args.pattern`, so a
tool call carrying no pattern made that `undefined !== undefined` and the guard
passed — grep then fell back to searching the renderer's own `query` across the
whole grant. `include` and `query` were never bound at all, letting a renderer
widen a search or silently narrow results the agent believes are complete. The
sibling glob case already had the `typeof` check, which is what made the
asymmetry clearly unintentional.

The IPC sender gate used `startsWith`, the exact pattern `isAppOrigin` warns
against 200 lines away ("that prefix-matches lookalike hosts"). It was safe only
because of a trailing slash. It now uses that helper, which also fixes a false
negative on an explicitly stated default port.

* fix(desktop): stop double sign-out, stranded retries, and redundant writes

Three correctness bugs from the same audit.

Menu Sign Out tore down the session directly instead of going through the
lifecycle coordinator, so it skipped the in-progress guard — and its own cookie
removal then tripped the coordinator's cookie watcher into a second concurrent
teardown, duplicating the sign_out event, the storage clear, and the /login
load. Teardown also existed as two divergent copies. The coordinator now
exposes `signOut()` and owns the single path; the menu just calls it. That
`tearDownSession` is no longer imported in index.ts is the check that it landed.

Offline recovery could strand permanently. The auto-retry loop stops itself
before calling `retry()`, and `retry()` never re-armed the load watchdog, which
is started once per window. So if a retried load hung — precisely what the
watchdog is for — no load event fired and no timer remained anywhere; the user
sat on the offline page until the window was closed. `retry()` now re-arms
before loading.

Pinned tabs were persisted on `did-navigate` and `did-navigate-in-page` for
every tab, pinned or not, with no change check, and the settings store compares
with `===` so a freshly built array never matched. Any single-page app
therefore triggered a synchronous mkdir + write + rename of the whole settings
file on the main thread on every route change — writing `[]` over `[]` when
nothing was pinned. The list is now fingerprinted, seeded at restore from what
is already on disk so the first navigation after launch is not a write either.

* fix(desktop): leaked timers, silent grep failures, and crashed tabs

Second pass on the audit backlog, all verified against tests that fail without
the change.

Every browser tool call leaked a timer. The watchdog raced the tool against
`sleep()`, which cannot be cancelled, so when the tool won — the normal case —
the timer stayed pending for the full window, up to two minutes, dozens deep
during an agent run. Replaced with a cancellable timeout cleared in a
`finally`; a test asserts the fake-timer count is unchanged across a call.

An invalid grep regex reported "no matches". A SyntaxError from `new RegExp`
returned an empty result set, which tells the model the string appears nowhere
in the user's files — a factual claim it acts on, when the search never ran. It
now fails as INVALID_REQUEST. The `safeRegex` guard moved out of the try while
there, since it was only inside it to be re-thrown.

A crashed tab wedged the session. Tabs left `tabs` only via close, so a dead
renderer stayed forever: `activeTab()` filtered it out while `activeTabId` still
named it, making `requireTab()` report "no page is open" with other tabs open,
and the panel went blank with no recovery. `render-process-gone` now drops the
tab, advances the active id, and reports session closure when it was the last.

`probeSession` cleared its abort timer inline after the await, so a thrown
fetch — the case the function exists for — skipped it. Moved to `finally`,
which also brings the body read inside the deadline.

One vanished file failed a whole directory listing: `Promise.all` over
per-entry `lstat` turned a single ENOENT into NOT_FOUND for the directory.
Churning directories like build output would intermittently fail to list.

Removed the `session-lifecycle -> browser-agent/driver` import edge, which
dragged the entire browser subsystem and its module-load `nativeTheme` listener
into the auth path to reach one four-line function. `clearBrowserProfile` is now
a required dependency wired from index.ts, which already owns both sides. Also
deleted `attachSessionLifecycle`, a compatibility wrapper with zero callers.

Added a channel-parity test between the preload bridge and the IPC table. They
share ~20 channel names as bare string literals with nothing tying them
together, so a typo on either side is a silently dead feature that type-checks
and ships. Verified it fails on a one-character change.

* fix(desktop): reach framed elements and harden the loopback sign-in

Two behaviour fixes from the audit backlog.

Interaction with same-origin iframes was broken. The snapshot deliberately
walks into those frames and hands the model ids for what it finds, but every
interaction then tested `instanceof HTMLInputElement` against the top frame's
constructors — false for nodes owned by a frame, because element wrappers are
realm-bound. So the driver reported a real `<input>` as "not a text input",
which took out framed login forms and editors that put a contenteditable body
in an iframe, such as TinyMCE. Framed selects reported "not a select" and
framed clicks skipped focus entirely. Checks now compare `tagName` or duck-type
the method being called, matching the realm-safe approach the credential guard
already used. The native value setter is taken from the element's own realm:
calling the top frame's setter on a frame's node throws "Illegal invocation".
Snapshot value reporting follows the same rule, which is safe because the
credential redaction above it is realm-safe and runs first.

The loopback sign-in server could be cancelled by anything on the machine. It
validated only the shape of the returned state, then tore the one-shot server
down and dispatched, leaving the real constant-time comparison to the callback.
So a request carrying any well-formed state killed an in-flight sign-in — and
the port is reachable by any local process and by any page the user has open
via a no-CORS GET, which cannot read the response but does not need to, since
the side effect is the kill. The state is now checked before anything is torn
down, and a Host that does not name the loopback is refused, which closes the
DNS-rebinding shape.

* refactor(desktop): drop duplicated helpers and stop logging query strings

Net -3 lines, and one of them was a real leak.

`navigation.ts` and `windows.ts` truncated URLs for their log lines with a bare
`.slice(0, 200)`, which keeps the query string — the five other log sites in the
app go through `scrubUrl` for exactly that reason. Tokens and signed parameters
live in query strings, so a blocked-URL warning could write one to disk. Both
now scrub.

`local-filesystem.ts` carried a private `isRecord` byte-identical to
`isRecordLike` in `@sim/utils/object`, and four more sites inlined the same
check. All now use the shared helper, which also tightens three of them: the
inline versions omitted the array exclusion, so an array satisfied a check that
then cast it to a record.

`tray.ts` hand-rolled slice-plus-ellipsis, the case `@sim/utils/string`'s
`truncate` exists for. Titles between 58 and 60 characters now get an ellipsis
where they previously did not — cosmetic, in a tray menu label.

Removed the `getTabsState` passthrough in the driver, a one-line re-export of
the session's own function, and renamed the session-level clear to
`clearProfileStorage`. `clearBrowserProfile` existed twice under one name, the
driver's being the composite that also clears the browsing-trail registry;
index.ts was already aliasing at the import to tell them apart.

Two things deliberately not done. The hand-rolled semver in updater.ts stays:
replacing it needs `semver` plus `@types/semver` as new declared dependencies
in the Electron main process, and the 90 lines it would delete are already
covered by eight assertions that I verified match the library's behaviour case
for case. Note the same prerelease comparison is duplicated in
apps/sim/lib/desktop/min-version.ts, so a future consolidation should do both.
No barrel for browser-agent either: routing `security-guards.ts` through one to
reach a single leaf function would pull the whole browser subsystem into its
module graph, which is the edge just removed from session-lifecycle.

* refactor(desktop): move browser compositing out of the session module

session.ts held five responsibilities in one flat namespace: 1,061 lines, 29
exports, 26 mutable module-level bindings. For contrast local-filesystem.ts is
a comparable 1,125 lines with two exports and no ambient state — size was never
the problem, the shared mutable namespace was.

Compositing is the part worth isolating. Where the native view sits, when it is
visible, which window owns it, the renderer bounds lease, and the occlusion
snapshot are the most intricate logic in the browser and are almost entirely
separable from tab bookkeeping. They now live in panel.ts (342 lines) and
session.ts is 792, with 15 bindings instead of 26.

The two modules were mutually dependent, which is what makes this kind of split
go wrong. Rather than events or a shared store, panel.ts takes the four things
it needs from the session through one PanelHost passed to initPanel — the same
shape as the existing initSession — so the import graph is one-way and there is
no new indirection to trace. Tab changes reach the panel by the session calling
layout(), exactly as before.

Two behaviours became explicit rather than implicit in the move:
detachIfAttached replaces callers reading `attachedView` to decide whether a
closing tab owns the surface, and isPanelVisible replaces `panelBounds !== null`.

Nothing about the split is verified by the split itself, so the bounds lease got
characterization tests first. It had none — there was not a single fake timer in
the suite — despite being the mechanism that hides the view when the renderer
crashes or wedges. Both tests were confirmed to fail against a broken lease
before the refactor began. The other 47 tests were not rewritten: only the
module their calls address changed, which is the useful signal that behaviour
was preserved.

Deliberately not split further. Focus tracking stays with tabs because it keys
off tab ids, and profile teardown stays put; separating either would be
taxonomy rather than decoupling.

* refactor: drop the legacy local_* filesystem tool shim

Granted folders are addressed through the ordinary VFS: the model calls
read/grep/glob against paths under user-local/, exactly as it does for
workspace files. A parallel local_read / local_grep / local_glob / local_list /
local_stat / local_mount_directory / local_list_mounts / local_forget_mount /
local_stage_file toolset existed alongside it, recognized but never advertised,
so an in-flight checkpoint written by an older desktop build could still finish.

There are no older desktop builds. apps/desktop is at version 0.0.0, the only
artifacts are a local 0.0.0 build, MIN_DESKTOP_VERSION is '0.0.0' meaning no
floor, and the app does not exist on staging at all — the v0.7.x tags are the
web app's. Nothing can have persisted a checkpoint naming these tools, and
nothing advertises them: they are absent from the generated tool catalog and
from mothership's catalog. The shim was defending against a past that never
happened.

Removes the name table, the legacy request builder, the server-side
LEGACY_READ_ONLY_TOOLS allowlist, the five local_* branches in the desktop
authorization switch, and nine display labels. isDesktopFilesystemToolCall
collapsed into isUserLocalVfsToolCall, which it had become a synonym for.

Two tests went with it. One asserted that local_list_mounts routes to the
desktop; the test immediately after it already covers the real path, an
ordinary read against a user-local path. The other asserted that legacy names
cannot open a folder picker, revoke a grant, or upload bytes — that property
now holds because no such tool name exists, which is a stronger guarantee than
refusing one.

* refactor(copilot): remove the plan/changelog VFS artifacts and workflow aliases

These beta surfaces are not a direction we are taking, so they come out rather
than staying behind a flag. Gone: the workflow alias modules (path resolution,
DB-backed resolver, .plans/.changelogs backing provisioning), the alias
materialization in the copilot VFS, the alias write paths in resource-writer
and workspace_file, the sandbox alias mounts in function_execute, the reserved
backing-path guards across mkdir/mv/create, and the alias resolution in the
chat home file picker.

xlsx survives but changes owner. It was gated twice across the repo boundary:
mothership's xlsx-writing flag gates the skill and prompt, while Sim gated the
compile path on mothership-beta. Those live in separate AppConfig applications,
so an operator had to flip two flags in two consoles, and off-hosted Sim fell
back to the MOTHERSHIP_BETA_FEATURES secret while the mothership half stayed in
Sim Cloud's AppConfig — split-brain across an ownership boundary. Mothership
controls whether the model ever learns xlsx exists, so if it is never offered
it is never requested and the second chokepoint only created a way for the two
halves to disagree. Sim's gate is removed; xlsx-writing is now the single owner.

With its last consumer gone, the mothership-beta flag and the
MOTHERSHIP_BETA_FEATURES secret are deleted. The two entries in the infra repo
are harmless until removed separately: they only inject an env var nothing
reads, and createEnv runs with skipValidation.

The reserved-system-file/folder concept goes with the aliases, since it existed
only to hide the backing rows. includeReservedSystemFiles and
includeReservedSystemFolders are removed rather than left as options every
caller passes true to. backingVfsPath is removed for the same reason — nothing
sets it once aliases are gone, so it was an always-undefined field on tool
results.

Test coverage is preserved rather than deleted with the feature.
resource-writer.test.ts looked alias-only but three of its eleven cases cover
the generic create path that survives; those are kept and the file retitled.
Two open_resource tests and one output-path test used alias-shaped strings
while asserting generic behavior; retargeted or dropped where a sibling already
covers it.

* refactor(copilot): remove the dead planArtifact column plumbing

copilot_chats.plan_artifact has no writer and no reader that does anything with
it. No client sends it, nothing renders it, and its whole history is fork-chat
and duplicate-chat plumbing faithfully copying a column that is always null —
the one change that might have populated it (mothership v0.8) was reverted.

Removed from the schema, the copilot API contract, the chat lifecycle column
sets, the fork route, superuser import, the data drain, the update-messages
write path, and the legacy chat detail response.

No migration here on purpose. The column stays in the database, orphaned and
null; dropping it is a separate deliberate step rather than something that
rides along with a code cleanup. Note that the next drizzle-kit generate will
now want to emit the DROP COLUMN, and check-migrations-safety will ask for it
to be annotated — that is the right moment to decide, not now.

Mothership never saw this field; it is Sim-side only.

* chore(copilot): sync the tool catalog for load_skill

Picks up the new load_skill tool plus the grep description that dropped its
stale reference to VFS "plans" entries. Generated from
copilot/contracts/tool-catalog-v1.json.

* refactor(copilot): follow the load_custom_tool rename to load_mcp_tool

Mothership renamed the loader once it was clear MCP was the only catalog kind
it could match, and dropped the single-valued `type` parameter. The two prompt
strings that teach the model the call shape are updated to
load_mcp_tool({ name }).

load_custom_tool stays in the UI hide-list next to load_agent_skill so tool
rows in historical transcripts keep rendering; nothing emits it any more.

* chore(copilot): sync the tool catalog and hide load_skill in the UI

load_integration_tool and list_integration_tools now publish route go/sync
instead of sim/async. Nothing changes in Sim's behavior — they always ran in
Go; the contract had been wrong.

load_skill joins the hidden tools. It is the same shape as the other loaders
already there: the agent pulling in a reference guide before doing the work is
a step toward the action, not the action. Sim's display-coverage test caught
that a newly added visible tool had no title or completed verb, which is the
guard working.

* fix(auth): handle session expiry in the app, not the desktop shell

The workspace auth gate is a Server Component, so it only re-evaluates on a
server render. A session that expired or was revoked mid-visit left the SPA
mounted and silently 401ing every request, with nothing to redirect it.

The desktop shell had grown its own detector for this: a 401 listener over
/api/*, a session probe, and a native "your session has expired" prompt. It
could only infer session state from cookie events and HTTP statuses, and it
inferred wrong — it fired on ordinary sign-outs (in-flight requests 401 during
teardown) and on launching already signed out (the window still shows the
restored route while the web app redirects). Those were nearly all of its
firings, since a 30-day sliding window means real expiry is rare.

Generalizes the impersonation-expired screen instead, which already had the
right shape: it keys off the session query settling to null after a session
that was live. A signed-out visitor never arms it, and `error` is excluded so
an offline blip cannot read as an expiry. The session query now refetches on
focus for every session, not just impersonation ones, so returning to a window
that slept through its session re-checks it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C54QHj4WPV777Fq2yRwkcb

* fix(copilot): port the scheduled-task and VFS fixes onto staging-v4

Replays the sim-side prompt-audit work on top of staging-v4.

complete_scheduled_task was filtered out of the execute route's response
payload, so an until_complete job could report completion and still be
rescheduled; the post-run bookkeeping now also refuses to revive a job that
already completed. Also clamps browser_wait_for's timeout the way the desktop
agent does, and replaces the oversized-read error's offset/limit advice, which
sent the model into a guaranteed retry loop.

* feat(desktop): let the model actually see browser screenshots

browser_screenshot captured an image and then threw it away. The renderer
stripped the data URL and substituted a note, and the tool's own description
told the model not to bother: "Dead end for perception." So the agent was
blind to anything not expressible as DOM text — canvas, charts, maps, images,
rendering and layout bugs.

The copilot has carried the machinery for this all along. A tool result shaped
as { content, attachment: { type: "image", source: { type: "base64", ... } } }
is serialized into a real image content block, with the media type sniffed from
the bytes rather than trusted from the declaration, and degraded to a text stub
when the routed model has no vision so the provider never 400s. The screenshot
result is now reshaped into that contract instead of discarded. A malformed
data URL still falls back to a note rather than shipping an attachment the
provider would reject.

Captures are bounded to a 1024px longest edge at quality 70. CDP clip.scale is
relative to CSS pixels, so this also sidesteps the device pixel ratio — an
unclipped capture on a retina display returns a 2x image, which was several
hundred kilobytes for no legibility the model could use.

The description is rewritten to bias toward visual questions only: appearance,
layout, rendering, charts, canvas. Reading content or finding something to
click stays with browser_snapshot, which is cheaper and returns the element ids
a screenshot cannot. That distinction is structural, not just advisory — having
seen the page does not let the agent act on it.

Companion change in mothership generalizes the tool-result inline-budget
exemption from "the read tool" to "any result carrying a model attachment".
Keyed on the tool name, an oversized screenshot fell through to the artifact
branch: the image was replaced by a reference the model cannot open, and the
result still reported success. Silent, and it would have hit almost every call.

* fix(desktop): polish browser panel and environment tray icon

* fix(desktop): enlarge environment tray markers

* fix(desktop): smooth environment tray markers

* refactor(copilot): consolidate resource mutation tools

* chore(copilot): clean up VFS follow-ups

* fix(desktop): round the dev tray marker

* feat(desktop): add integrated terminal resources

* Fix electron app resize causing glitchy browser frames

* feat(copilot): add persistent tool permissions

* fix(copilot): retire stale tool permission prompts

* fix(desktop): keep terminal rendering responsive

* fix(desktop): preserve resource rendering continuity

* feat(desktop): add browser tab duplication actions

* feat(desktop): add terminal tab context actions

* fix(desktop): allow browser agent localhost navigation

* feat(desktop): add tmux-backed terminal sessions

* fix(desktop): restore terminal scrollback per view

* chore(copilot): sync updated wait tool contract

* poll terminal session state for non regular shells

* add terminal right click menu

* feat(desktop): add terminal handoff and key batching

* fix(desktop): reserve the traffic-light lane from the platform

macOS draws the window controls itself, at a fixed physical size, above all web
content. The page renders full-bleed beneath them, so it has to reserve that
lane — and it did so with five hardcoded CSS pixel values. CSS pixels scale with
page zoom and the OS-drawn lights do not, so zooming out shrank the reservation
until the lights were drawn over the sidebar toggle, and the header row below
sat inside their band.

Electron's `titleBarOverlay` publishes the controls' real geometry to the page as
the `titlebar-area-*` env vars, which Chromium rescales per zoom so a reservation
derived from them holds its physical size. Measured across zoom 0.58-1.2, the
reserved area stays within ~0.6 DIP, the residual coming from env values being
quantized to whole CSS pixels.

Every lane length now derives from those vars, so the login route and the
mothership content offset were fixed without being touched — they already read
`--desktop-title-bar-height`. Two of the replaced constants were also simply
wrong: the platform reports the lane at 38px and the safe area at 81px, against
the hand-measured 36 and 83.

The toggle keeps a constant physical size beside the lights, expressed as a
proportion of the lane rather than in pixels: a px literal would scale with zoom,
and calc cannot divide a length by a length to recover a scale factor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C54QHj4WPV777Fq2yRwkcb

* fix(desktop): avoid transient terminal tab labels

* feat(copilot): attach browser and terminal tab context

* feat(desktop): close tmux panes from terminal tools

* fix(desktop): keep terminal tab icons stable

* add right click to browser and cleanup terminal right click options

* fix(desktop): reduce hidden panel background work

* perf(desktop): shrink browser panel snapshots

* perf(desktop): reduce terminal main process overhead

* perf(terminal): pause work for hidden sessions

* fix session arch for desktop

* fix(desktop): replace exited terminal sessions

* feat(copilot): persist desktop resources across chats

* fix(emcn): keep resource tab widths consistent

* fix(copilot): restore active client panels

* feat(desktop): import Chrome browser data

* fix(copilot): close resources before chat creation

* feat(desktop): suggest imported browser sites

* fix(desktop): autofill identifier-first sign-ins

* fix resizing issues + cookies source

* fix visits marking

* chore(db): drop branch migrations ahead of staging merge

0264/0265 on this branch collide with staging's 0264-0270 on both the
journal idx slots and the meta snapshot filenames. Reverting the migration
artifacts to the merge-base lets staging's chain merge cleanly; schema.ts
keeps the copilot changes and drizzle-kit regenerates a single migration on
top of 0270 after the merge.

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

* feat(db): regenerate copilot tool-permission migration on top of staging

Replaces the branch's old 0264/0265 (dropped pre-merge so staging's
0264-0270 chain could apply cleanly) with a single 0271 generated against
staging's schema: the permission-decision enum, the two
copilot_async_tool_calls decision columns, and copilot_chats.auto_allowed_tools.

Deliberately does NOT drop copilot_chats.plan_artifact. The branch removed
every reader, but the currently-deployed code still SELECTs that column, so
dropping it in the same deploy breaks the old app version during blue/green
overlap — `check:migrations` flags it for exactly this reason, and the honest
fix is to defer rather than annotate around it. The column is retained in
schema.ts marked @deprecated; drop it in a follow-up once this has rolled out.

Also in this commit, all fallout from the merge itself:
- pinned-fetch/revoke tests: their private-IP stub moved to @sim/security/ssrf
  alongside the source change. Worth noting the stub exists because the suite's
  203.0.113.10 is TEST-NET-3, which the real classifier correctly calls
  reserved — the old stub had been quietly disagreeing with production.
- materialize-file test: dropped the reserved-system-folder case, which covered
  the workflow-alias backing folders this branch deleted.
- api-validation route ratchet 977 -> 983 (this branch's new routes).

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

* add cmd f

* review pass

* chore(db): drop branch migration ahead of staging merge

Both sides independently claimed idx 0271, so the snapshot and journal would
conflict add/add. Ours is plain additive DDL that drizzle regenerates from
schema.ts; staging's is a hand-written CONCURRENTLY index build that cannot be
regenerated. Dropping ours and re-generating on top of staging's is the only
order that preserves both.

schema.ts is deliberately untouched — it is the source of the regeneration.

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

* chore(db): drop branch migration ahead of staging merge

Both sides independently claimed idx 0272, so the snapshot and journal would
conflict add/add. Ours is plain additive DDL (one enum, two columns, one jsonb
default) that drizzle regenerates from schema.ts; staging's is a hand-written
migration with DO blocks and CONCURRENTLY index builds that cannot be
regenerated. Dropping ours and re-generating on top of staging's is the only
order that preserves both.

schema.ts is deliberately untouched — it is the source of the regeneration.

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

* style(db): biome-format the regenerated migration metadata

drizzle-kit emits _journal.json and the snapshot with expanded arrays, which
biome check rejects. The merge commit used --no-verify, so lint-staged never
formatted them and CI's lint step failed on exactly these two files.

Whitespace only — both files are byte-identical under `jq -S -c`.

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

* fix(desktop): pin the platform in the OS-auth tests

promptForSecret gates Touch ID on process.platform === 'darwin'. The suite
mocked electron's systemPreferences but inherited the runner's real platform,
so the eight biometric expectations passed on a Mac and failed on Linux CI,
where every call fell through to the confirmation dialog instead.

Pins the platform per-test and restores it after, and adds a case for the gate
itself — the branch whose absence from the suite is what let this through.

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

* fix(desktop): refine environment dock icons

* fix(desktop): align packaged environment icons

* fix(desktop): keep packaged dock icon rendering consistent

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Theodore Li <theo@sim.ai>
2026-07-28 19:25:59 -07:00
WaleedandWaleed Latif a042f0bd0f chore(deps): fix OTel version split, drop dead deps, declare emcn peers (#5994)
* chore(deps): fix OTel version split, drop dead deps, declare emcn peers

- Pin @opentelemetry/{resources,sdk-metrics,sdk-trace-base,sdk-trace-node}
  to exact 2.7.1 so they match sdk-node's pins instead of floating to 2.8.0.
  The carets meant app code built spans with 2.8.0 and passed them into
  NodeSDK from 2.7.1, which only worked by duck-typing.
- Declare the 13 packages @sim/emcn imports but never declared, as peers
  mirrored into devDeps. @radix-ui/react-dismissable-layer had no
  declaration anywhere in the repo and resolved only transitively.
- Remove ffmpeg-static: its binary downloads via postinstall, but it is not
  in trustedDependencies and Docker installs with --ignore-scripts, so the
  accessSync branch never succeeded and both call sites always fell through
  to system ffmpeg.
- Remove critters + experimental.optimizeCss: Next only loads critters from
  the Pages Router renderer, and apps/sim is App Router only.
- Make simstudio-ts-sdk zero-dependency by dropping node-fetch for native
  fetch; engines >=18.
- Remove unused @vercel/og and postgres from docs, dotenv/inquirer/listr2
  from the CLI, and yaml from the root.
- Move @aws-sdk/client-appconfig from the root to apps/sim, its only consumer.
- Delete the apps/sim overrides block; Bun only honors top-level overrides.
- Bump free-email-domains 1.2.25 -> 1.9.70 (4,779 -> 13,059 domains).
- Validate object/array variables with JSON.parse instead of JSON5, matching
  what the executor actually parses.
- Swap the changelog GitHub icon off lucide to GithubOutlineIcon, matching
  the navbar chip on the same page.
- Unify @types/node on 24.2.1 and lucide-react on ^0.511.0; bump chalk to 5
  and image-size to 2.

* fix(deps): complete the OTel pin, restore SDK error detail, revert email list

Follow-ups from an independent audit of the previous commit.

- Pin @opentelemetry/sdk-node and the three otlp-http exporters to exact
  0.217.0. Pinning only their four dependents was self-reversing: sdk-node
  0.219.0 requires core 2.8.0 exactly, so the next update would have
  silently rebuilt the split this PR removes.
- Declare @opentelemetry/core (2.7.1). It is imported by
  lib/copilot/request/go/propagation.ts but resolved only by hoisting, and
  it is the OTel package with the most version churn in the tree.
- Pin @radix-ui/react-dismissable-layer to exact 1.1.13 in @sim/emcn. All
  five transitive parents pin it exactly; a caret would fork a second copy
  on 1.1.14, which is the duplicate-context bug the declaration prevents.
- Surface error.cause in simstudio-ts-sdk. Native fetch reports network
  failures as a bare "fetch failed" and puts the reason on cause, so every
  DNS/TLS/refused error was reaching callers with no diagnostic content.
- Revert free-email-domains to 1.2.25. Upstream now merges the free-domain
  list with two disposable-email blocklists, so 1.9.70 classifies real
  organization domains as free — UK charities, some companies and
  universities, and the JP/KR ISP domains APAC SMBs use for business mail.
  The demo form blocks submission on that check, so a false positive costs
  the booking entirely. Worth doing deliberately, not inside a deps change.
- Lower packages/cli engines to >=18. chalk 5 and commander 11 both accept
  >=16 and the source uses no Node 20 API, so >=20 only produced EBADENGINE
  for Node 18 users.

---------

Co-authored-by: Waleed Latif <waleed@simstudio.ai>
2026-07-27 19:16:32 -07:00
d24bc7eccb feat(agent-stream): thinking and tool streaming (#5671)
* feat(agent-stream): add agent-events thinking/tool streaming for chat and canvas

Ship the agent-events-v1 protocol with provider tool loops, dual-gated chat thinking, DeepSeek/Groq/OpenAI reasoning wiring, and ChatGPT-like thinking chrome.

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

* fix(agent-stream): clear stuck streaming UI and format db snapshot

Biome was failing CI on migrations/meta/0261_snapshot.json. Also settle
assistant streaming/tool flags when SSE ends without a terminal frame,
without clobbering Stop's finalized content.

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

* fix(agent-stream): satisfy biome format and import order

Auto-format the sim package for CI lint:check, and repair the Anthropic
streaming tool-loop payload after an unsafe delete-to-undefined rewrite.

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

* fix(agent-stream): keep drained answer on abort and update migration journal test

Treat AbortError from reader.cancel as a cancelled pump result so soft-complete
retains answerText. Point the workspace storage migration journal assertion at
0261_chat_include_thinking.

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

* fix(chat): keep Stop notice when server emits cancel error

Ignore terminal SSE error frames after the user aborts so
"Client cancelled request" cannot overwrite "Response stopped by user".

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

* improvement(chat): ChatGPT-style thinking shimmer and stick-to-bottom scroll

Add left-to-right shimmer on live thinking label/body, keep scroll working by
shimmering an inner node, and follow the answer only while near the bottom.

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

* fix(agent-stream): stop pump on client disconnect; soft-complete agents only

Abort the agent stream pump when the projected HTTP body is cancelled so
provider work does not continue after disconnect. Limit AbortError soft-success
to Agent blocks so Function/HTTP cancels still fail in logs.

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

* fix(agent-stream): persist includeThinking across pause snapshots

Paused chat runs with Include thinking enabled were dropping the flag when
serializing the pause snapshot, so resume always rebuilt streams without
thinking/tool SSE frames.

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

* fix(agent-stream): keep drained answer text when stream times out

Persist pump answerText onto the streaming execution before throwing on
timeout, and carry that partial content into the failed block output so
logs match what the client already saw.

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

* improvement(chat): auto-collapse tools chrome when tool streaming ends

Match thinking UX: open while tools run, collapse when finished, and keep
the panel open only if the user manually reopens it.

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

* fix(agent-stream): settle canvas stream chrome on failure paths

Clear agentStreamActive and settle running tool chips when blocks error,
timeouts cancel runs, or execution ends without stream:done so the output
panel does not stay on live Thinking/Using tools chrome.

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

* fix(lint): organize imports in terminal console store

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

* fix(agent-stream): mark open tools cancelled on HITL pause

Pause can interrupt a tool loop without tool end events; settling those
chips as success incorrectly showed unfinished tools as complete.

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

* chore(db): drop branch-local 0261 migration ahead of staging merge

* chore(db): regenerate include_thinking migration as 0266 post staging merge

* fix(providers): resolve type errors in streaming tool loop call sites

* fix(agent-stream): gate agent events opt-in and correct provider loop behavior

- streamToolCalls and provider thinking requests now require run-level
  agentEvents opt-in (canvas on, chat dual-gated, API off) so existing
  runs keep pre-agent-events behavior exactly
- OpenAI reasoning summaries opt-in + strip-and-retry on unverified-org 400
- streaming loops run tool postProcess again (firecrawl/exa async results)
- bedrock live loop falls back to silent path for responseFormat
- deepseek: reasoning_content pass-back unconditional, 'none' sends disabled
- groq: x_groq.usage fallback, reasoning params gated, qwen none disables
- gemini: functionCall parts echoed verbatim, local ids only for events
- truncated turns (max_tokens/length) no longer execute partial tool calls
- MAX_TOOL_ITERATIONS exit flushes last turn text as final answer
- iterations reports actual model calls; shared loop plumbing extracted

* refactor(agent-stream): consolidate protocol, dedupe client/server plumbing, hygiene

- canonical ChatStreamFrame union + type guards consumed by server emitters
  and the chat client; stream_error restored to legacy log-only handling
- strip thinking/tool args from providerTiming on public final envelopes
- shared tool-chip lifecycle module for chat, canvas, and console store
- shared sink-to-execution-events forwarder replaces the copy-pasted
  adapter in the execute route and HITL manager; LIVE_ONLY event set shared
- stream:thinking payload field renamed data->text; canvas thinking batched
- abort reasons carried as AbortError DOMExceptions so raw fetch consumers
  classify correctly; thinking cap renamed to chars and scope-documented
- kimi wired for agent events like the other compat providers
- deleted dead exports/step-N comments; fixtures match real wire shapes;
  loop tests use explicit mocks instead of importOriginal

* test(agent-stream): cover the dual-gated execution path and typed abort reasons

- chat route tests assert agentEvents reaches executeWorkflow only when
  policy and protocol header agree
- execution-limits tests assert AbortError-typed reasons
- executor metadata type carries agentEvents

* fix(deploy-modal): align include-thinking spacing with the modal's 6.5px rhythm

* docs(agent-stream): autogenerate per-model thinking/tool stream support on the Agent block page

- capabilities.thinking.streamed ('full' | 'summary' | 'none') on models.ts,
  explicit for the Anthropic family where visibility varies per generation;
  getThinkingStreamVisibility exposes the derivation for docs and UI alike
- scripts/sync-agent-stream-docs.ts regenerates the support tables between
  markers in workflows/blocks/agent.mdx from the model registry and
  STREAMING_TOOL_CALL_PROVIDERS; --check fails on drift or missing metadata
- wired agent-stream-docs:check into CI next to the other sync gates

* feat(anthropic): request summarized thinking display for omitted-default Claude models

The newest Claude generations (Fable 5, Sonnet 5, Opus 4.8/4.7) default
thinking.display to omitted — empty thinking blocks, no deltas. On
agent-events runs Sim now opts back in with display: 'summarized', driven
by the registry's streamed metadata; legacy runs keep the exact
pre-agent-events request shape. Registry, generated docs, and the family
capability table updated accordingly.

* docs(skills): cover thinking.streamed and agent-stream docs sync in model skills

* chore(deps): upgrade @anthropic-ai/sdk to 0.114.0 and adopt official types

- adaptive thinking, display, and output_config are now SDK-typed; the only
  remaining custom payload field is output_format (beta-header structured
  outputs, which the SDK models as output_config.format instead)
- anthropic stream events narrow on the SDK's discriminated unions instead
  of anonymous casts; compat deltas type content/tool_calls from the OpenAI
  SDK with vendor reasoning fields as an explicit optional extension
- @sim/auth exposes an explicit VerifyAuth contract so its declarations no
  longer reference better-auth's nested zod instance (TS2883 under fresh
  install layouts); realtime consumer aligned
- docs app zod pinned to the repo's exact 4.3.6 so ai SDK types bind the
  same zod instance (docs type-check was latently broken)
- knowledge embedding tests made hermetic against local .env keys and
  hosted rotation fallback

* refactor(providers): replace legacy as-any stream casts with annotated typed casts

* refactor(providers): finish provider audit — remove dead byte-stream helper, annotate remaining legacy casts

Audit of all 26 providers for the agent-events feature confirmed every
streaming execution declares agent-events-v1 and every adapter emits
AgentStreamEvent objects. Cleanup from the audit: the unconsumed legacy
createOpenAICompatibleStream byte helper is deleted, and the remaining
streamResponse-as-any casts (xai, nvidia, kimi, meta, zai, sakana) are
annotated typed casts matching the groq/deepseek fix.

* feat(streaming): stream answer text live during tool loops via turn_end protocol

The live tool loops buffered all answer text per model turn (classification
of intermediate vs final is only known at turn end), so gated surfaces saw
thinking stream, then dead air with the thinking chrome stuck open, then the
whole answer at once.

Loops now emit text deltas live as `turn: 'pending'` plus a `turn_end`
event per turn. The pump buffers pending text and projects it to the byte
path (answerText/logs/memory/legacy clients) only on a final turn_end, so
all settled semantics are unchanged. Gated surfaces render the pending text
as it streams and reconcile with a reset when a turn resolves to tools:

- public chat: live `chunk` frames from the sink + dual-gated `chunk_reset`;
  byte-path frame emission is suppressed to avoid duplicates (kept for
  response-format transformed streams via clientStreamTransformed)
- canvas: forwarder emits live `stream:chunk` + `stream:chunk_reset`; the
  execute route and HITL resume readers stop re-emitting byte chunks; panel
  chat tracks per-block segments and replaces content on flush
- chat client: per-block text segments, chunk_reset handling, and thinking
  chrome now settles on tool start as well as first answer chunk

* fix(streaming): address validated review findings across provider gating and reset reconciliation

Three-reviewer pass over the branch, findings validated against staging:

- agent-handler forwards agentEvents to executeProviderRequest — the flag was
  computed but dropped in the field-by-field copy, so provider-side thinking
  requests (OpenAI summaries, Gemini includeThoughts, Anthropic summarized
  display) never activated on opted-in runs
- openai: restore summary:'auto' alongside explicit reasoning effort — staging
  always paired them; gating summary purely on agentEvents changed legacy
  payloads
- gemini: Gemini 2 + tools + responseFormat falls back to the silent path;
  the live loop never applied the deferred responseSchema for AUTO tools
- openai-compat loop: malformed tool-argument JSON fails the call instead of
  executing with defaulted {} args (staging parsed inside the execution try)
- openai-compat parser: a vendor id arriving after a synthesized start no
  longer renames the call (start/end ids stayed consistent)
- stream-pump: abort closes the byte projection so a drain blocked on
  backpressure cannot deadlock teardown
- chunk_reset removes the block from the client text order (deployed chat +
  panel chat) so a reset block re-registers at arrival position — fixes
  separator/order corruption when parallel blocks stream around a reset
- resume route echoes the negotiated X-Sim-Stream-Protocol response header
  (parity with the chat route); docs: [DONE] wire shape + final-vs-error
  terminal semantics corrected

* chore(deps): exempt pinned @anthropic-ai/sdk 0.114.0 from the release-age gate

CI's bun install --frozen-lockfile blocks 0.114.0 (published 2026-07-23,
younger than the 7-day supply-chain gate). The pin is exact and was vetted
for the agent-events streaming work; following the existing bunfig pattern,
the exclusion ages out on 2026-07-30 and should be dropped then.

* chore(providers): fix double-cast-allowed annotation placement for the strict boundary audit

The audit only recognizes the annotation on the line directly above the cast;
two annotations had drifted behind intervening code lines (groq stream params,
deepseek loop messages) and the OpenAI reasoning-summary widening cast was
never annotated. No behavior change.

* fix(chat): settle straggler tool chips as error when final reports failure

A failed run can still terminate with a `final` frame carrying success: false;
running chips previously settled green regardless of the outcome.

* fix(canvas): wire agent stream chrome into run-from-block

Run-from-block executions emit the same live stream:thinking/stream:tool
events as full runs but registered none of the handlers, so the terminal
never showed thinking or tool chips on that path. The per-run chrome
(batched thinking writes + tool chip lifecycle + settlement on stream done,
block error, and every terminal execution state) is extracted into a shared
createAgentStreamChrome factory consumed by both paths.

---------

Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-07-23 19:39:03 -07:00
Waleed 78fb2c0679 chore(deps): bump next to 16.2.11 to clear security advisories (#5890)
* chore(deps): bump next to 16.2.11 to clear security advisories

Patches SSRF, cache confusion, DoS, and middleware-bypass advisories
(GHSA-89xv-2m56-2m9x et al.) affecting next < 16.2.11 across apps/sim,
apps/docs, and packages/emcn. Excludes next/@next/env from the
minimum-release-age gate until the 7-day window elapses on 2026-07-28.

* chore(deps): drop aged-out typescript entries from release-age excludes

typescript and @typescript/typescript6 passed the 7-day minimum-release-age
gate (aged out 2026-07-15 and 2026-07-13), so their exclusions are no longer
needed. Keeps @typescript/native-preview (permanent nightly builds) and the
Pi packages (age out 2026-07-24).
2026-07-23 10:47:07 -07:00
Waleed fc25cfb3c8 fix(docs): fix Core Web Vitals regressions on docs.sim.ai (#5630)
* fix(docs): fix Core Web Vitals regressions on docs.sim.ai

Empirically measured under real trace-based (devtools) CPU/network
throttling against the live site: mobile Performance 59, LCP 9.2s
(TTFB 745ms + 8.4s element render delay).

- sidebar-components.tsx / [lang]/layout.tsx: the docs sidebar renders
  every page in the doc tree as a link at once. Next's default
  viewport-prefetch fired an RSC payload fetch for every one of them on
  initial load - dozens of concurrent requests competing with the page's
  own content for bandwidth. Wired fumadocs' documented `sidebar.prefetch`
  option through to the custom SidebarItem/SidebarFolder components (which
  were bypassing it entirely, using next/link directly with no prefetch
  prop) via the `useSidebar()` context hook.
- video.tsx: `autoPlay` forces browsers to fetch the full video file
  immediately on mount regardless of `preload`. Gated actual src loading
  behind an IntersectionObserver so a page with several of these doesn't
  pull down every video up front (5MB across 3 requests, in this case).
  Single shared component - fixes every doc page that embeds one.
- proxy.ts: the i18n middleware matcher excluded favicon/robots.txt/etc
  but not `icon.svg`, so every request for it got routed through i18n
  negotiation instead of served as a static file, 404ing in production.
- next.config.ts: enable productionBrowserSourceMaps - safe since this
  repo's source is already fully public, real debuggability benefit,
  zero performance cost.
- shiki 4.0.0 -> 4.3.1 (verified: syntax highlighting still renders
  correctly). Attempted a coordinated fumadocs-core/ui/mdx/openapi
  upgrade to latest; fumadocs-openapi's v11 factory function became
  client-only (breaking change beyond its declared peer deps, requiring
  a component-boundary restructure), so only the safe, verified,
  docs-exclusive bumps (fumadocs-core/ui/mdx, shiki) are included here -
  the openapi major bump needs its own dedicated migration PR.

Verified via a real production build (dummy env, all 3974 pages
including API reference render/build cleanly) and a clean (non-stale)
local server: Performance 59 -> 71 measured under real devtools
throttling, RSC prefetch requests 63 -> 11, video requests/bytes 3/5MB
-> 0. A pre-existing React hydration warning (#418) was found and
confirmed present on live production before any of these changes,
unrelated to this diff - documented, not blocking.

* fix(docs): fall back to eager video loading without IntersectionObserver

The lazy-load gate from the previous commit threw before isInView could
ever become true in environments lacking IntersectionObserver (older
browsers, some embedded webviews), leaving videos permanently
source-less instead of falling back to eager loading.

* chore(docs): drop non-TSDoc inline comments

Repo convention is TSDoc-only, no plain // comments.

* fix(docs): accessibility and SEO defects across the docs app

Audited with parallel subagents against the accessibility and SEO skill
checklists, each fix verified by reading the actual code (not assumed):

Accessibility:
- lightbox.tsx: focus was never captured/restored on close, and Tab
  escaped the modal to the page behind it (no focus trap on the single
  focusable element)
- heading.tsx: the per-heading copy-link icon only appeared on hover,
  invisible to keyboard-only navigation (added peer-focus-visible)
- navbar.tsx: active nav tab had no aria-current
- response-section.tsx: the status-code dropdown had no
  aria-haspopup/aria-expanded/role, and no Escape-to-close
- workflow-preview.tsx: same focus-trap gap as lightbox.tsx on the
  expanded-canvas modal

SEO:
- page.tsx: generateMetadata's hreflang/canonical URLs used a naive
  String.replace to strip the locale prefix, which also matched "/en"
  inside unrelated slugs (platform/enterprise, integrations/enrich,
  platform/self-hosting/environment-variables), corrupting those pages'
  canonical and alternate-language URLs. Replaced with a prefix-only strip.
- structured-data.tsx: the SoftwareApplication JSON-LD block compared
  url === baseUrl (no trailing slash) against the homepage's actual url
  (always has a trailing slash), so the condition was always false and
  this structured data never rendered anywhere, including the homepage.
- structured-data.tsx: "Mothership" in the indexed featureList violated
  the constitution's required language (the agent is "Sim", the surface
  is "Chat") - this ships in JSON-LD search engines parse.

* fix(docs): defer the Ask Sim chat widget's heavy deps until opened

The chat panel (useChat from @ai-sdk/react, Streamdown + its CSS) was
mounted unconditionally in the root layout on every single page, so
its full weight loaded and executed even though the widget starts
closed on every page view.

Traced via the LCP breakdown insight under real devtools CPU/network
throttling: the LCP text element (the intro paragraph) had a ~8s
element render delay despite a ~13ms TTFB, and bootup-time attributed
~4.3s of scripting time to a single chunk containing React/ReactDOM's
own runtime plus this widget's eagerly-bundled dependencies.

Split into a lightweight ask-ai.tsx (just the toggle button + open
state) and ask-ai-panel.tsx (the actual chat UI, useChat, Streamdown),
loaded via next/dynamic(..., { ssr: false }) only when the user opens
the widget. Verified: the panel's chunk now has zero network requests
on initial page load.

Measured (mobile, devtools throttling, /introduction):
- Performance: 69 -> 75
- LCP: 8.0s -> 6.4s
- TBT: 260ms -> 130ms

The remaining ~6.4s LCP delay traces to the same shared chunk, now
identified as core React/ReactDOM hydration cost for this page's
sidebar/TOC/breadcrumb tree rather than an isolated bug - a real,
larger initiative (hydration architecture, not a surgical fix),
documented here rather than rushed.

* fix(docs): preserve Ask Sim chat state across close/reopen

The panel split unmounted AskAIPanel entirely on close, discarding
useChat's message state - reopening always started an empty
conversation, unlike the original single-component layout where
useChat lived in a component that never unmounted.

Fixed by keeping the panel mounted (via a hasOpened flag that never
resets) once first opened, and having the panel itself return null
when closed rather than being conditionally removed from the tree by
its parent - hooks still run every render, so useChat's state persists
across visibility toggles. The dynamic import still only fires on the
first open, so the initial-load win is unchanged.

Verified via a real click-through (open, type, close, reopen): input
persists correctly, and the panel chunk still has zero network
requests on initial page load. Performance unchanged at 75.

* chore(docs): lint fixes (import order, formatting)

* fix(docs): fill the Ask Sim UI gap while the panel chunk loads

handleOpen set open=true synchronously, hiding the trigger button
before the dynamically imported panel had a chance to render anything
(next/dynamic renders null by default with no loading option) - on a
slow connection neither the button nor the panel was visible.

Added a loading fallback in the same fixed position so there's no gap
between the button disappearing and the real panel appearing.
2026-07-13 09:10:33 -07:00
Waleed d078c84ee6 chore(typescript): upgrade to TypeScript 7 (native Go compiler) (#5521)
* chore(typescript): upgrade to TypeScript 7 (native Go compiler)

Bumps typescript to ^7.0.2 across every workspace package. Full
bun run type-check/lint/build/test all pass; apps/sim's type-check
(the one needing an 8GB heap bump) drops from ~55s to ~7s wall time.

Migration fixes required by TS7's stricter defaults:
- baseUrl removed: drop it from 5 tsconfigs (paths already resolved
  relative to tsconfig dir, so behavior is unchanged) and prefix the
  one bare (non-relative) paths entry each in apps/sim and
  apps/realtime with './'
- moduleResolution=node10 removed: switch packages/cli and
  packages/ts-sdk to "bundler", matching the rest of the monorepo
- types now defaults to [] instead of auto-including every @types/*
  package: add "types": ["node"] to the shared base tsconfig (this
  is fundamentally a Node monorepo, so this restores prior behavior
  in one place instead of duplicating it per-package), add explicit
  @types/node deps to packages that now rely on it transitively via
  @sim/db/@sim/logger, and add "declare module '*.css'" to the two
  packages with plain (non-module) CSS side-effect imports that
  TS7's stricter checker now flags
- packages/logger's isomorphic `typeof window` check no longer needs
  DOM lib in every consumer: replaced with `'window' in globalThis`
- packages/testing and apps/realtime's fetch/DOM mocks need DOM lib
  where they're compiled, since they model the browser Fetch API
- the `typescript` npm package no longer exports the classic
  Compiler API from its main entry (moved to unstable/ast subpaths);
  apps/sim's Function-block route used it at runtime to strip
  import statements from user code, so that one call site now uses
  Microsoft's official transition package, @typescript/typescript6
- Next.js 16.2.6's own TypeScript-detection heuristic hardcodes a
  path TS7 no longer ships, and its auto-install fallback assumes
  npm/pnpm; added @typescript/native-preview as a devDependency to
  apps/sim and apps/docs so Next detects a valid native compiler
  instead of trying (and failing) to auto-install one

Not merging yet: TS 7.0.2 published today and is still inside this
repo's bunfig.toml minimumReleaseAge (7-day) supply-chain gate, so
`bun install` will fail for everyone until 2026-07-15. Opening this
now to get it through review; hold the actual merge until then.

* fix(typescript): address Greptile review findings on TS7 upgrade

- packages/logger: 'window' in globalThis treats a shim that leaves
  globalThis.window explicitly undefined as browser-only, silently
  dropping production server logs. Restore the original
  typeof !== 'undefined' semantics via an inline cast instead, so it
  stays correct without requiring DOM lib in every consumer.
- packages/ts-sdk, packages/cli: both are tsc-built, published as
  Node ESM (package.json "type": "module" with an "exports" map).
  "moduleResolution": "bundler" is too permissive for that target -
  it accepts import patterns (e.g. extensionless relative imports)
  that Node's actual ESM resolver rejects at runtime. Switch both to
  "module"/"moduleResolution": "nodenext", the correct pairing for a
  published Node ESM package. Verified real tsc builds (not just
  --noEmit) still succeed for both.

* chore(bunfig): temporarily disable minimumReleaseAge gate for TS7 install

TS 7.0.2 published today, still inside the 7-day gate. Lowering to 0
to unblock this merge; will restore to 604800 in an immediate follow-up
commit right after merging.
2026-07-08 16:41:23 -07:00
Waleed 7545391cb3 feat(docs): render workflow previews with the shared editor renderer (#5277)
* chore(workflow-renderer): declare @sim/emcn dep + wire the package into docs

Adds the missing @sim/emcn peer/dev dependency to @sim/workflow-renderer (it imports @sim/emcn in every View but resolved only via workspace hoisting). Wires apps/docs to consume @sim/workflow-renderer (dependency, transpilePackages, Tailwind @source) and adds remark-breaks (pulled transitively via the barrel's NoteBlockView export) — mirroring the @sim/emcn integration. Foundation for migrating the docs workflow-preview fork onto the shared Views. Build resolves the package/@source/remark-breaks cleanly.

* feat(docs): render loop/parallel containers with the shared SubflowNodeView

Replaces the forked PreviewContainerNode with a thin DocsContainerNode that maps the static preview data to SubflowNodeView's read-only (isPreview) props — no stores or hooks. Adds the block size to the preview node data so the view can size itself, and corrects the parallel example's start-edge handle id to 'parallel-start-source' (the view derives the handle id from kind). Deletes preview-container-node.tsx. Container colors/icons are now owned by the shared view (loop=blue, parallel=yellow).

* feat(docs): render block nodes with the shared WorkflowBlockView

Replaces the forked PreviewBlockNode with a thin DocsBlockNode that maps the static preview data to WorkflowBlockView's props — store-free, builds the subblock rows (condition/router Context+routes/default + tools + error) via SubBlockRowView, strips branch-id prefixes so the view's regenerated handle ids match, remaps router->router_v2, and keeps the framer-motion dim/stagger wrapper. Promotes resolveIcon into block-icons.tsx, adds the --workflow-edge token to docs global.css, deletes preview-block-node.tsx. The canvas diagrams now render with the real editor's view.

* refactor(workflow-renderer): make editor-only WorkflowBlockView props optional

The child-deploy, schedule, and webhook badge props (and their callbacks) only matter in the editor. Mark them optional and optional-chain the three callbacks so read-only consumers (docs, academy) can omit the whole group instead of passing ~18 explicit off-values. The editor still passes them, so its behavior is byte-identical (verified: apps/sim type-check clean). DocsBlockNode drops the off-props.

* feat(docs): replace how-it-runs static diagrams with live WorkflowPreview

Swaps the four static PNGs on the how-it-runs page for live, app-styled WorkflowPreview diagrams (concurrency, combination, condition+router branching, error path). Adds the four example workflows and renders error-port edges red to match the editor. The English page only; the translated execution/basics pages keep the PNGs.

* refactor(workflow-renderer): the view owns condition/router/error rows

Both the editor container and the docs adapter hand-built the condition/router/error summary rows in an order that had to stay in lockstep with the view's absolute handle-offset math — a three-way coupling with nothing enforcing it. The view now renders those rows itself from the conditionRows/routerRows it already receives (plus a routerContextValue prop for the router's Context row), so row order and handle geometry live together in one place. Both containers pass only data and their non-branch rows.

Editor is byte-identical: getDisplayValue moves to where conditionRows/routerRows are built; the no-subBlock SubBlockRow path is already an exact SubBlockRowView(title, value) passthrough; the error row stays gated on shouldShowDefaultHandles. Verified apps/sim type-check clean. Docs now also renders the error row on condition/router blocks, which the real editor already did (shouldShowDefaultHandles is true for them) — an alignment fix.

* refactor(docs): drop the parallel --wp-* token layer for the app/emcn tokens

The workflow-preview ran a 25-token --wp-* mirror (22 were pure aliases of app tokens docs already defines) plus a .wp-scope wrapper class. Replaces every var(--wp-X) with its canonical app/emcn token (--wp-edge->--workflow-edge, --wp-highlight->--brand-secondary, badges->--badge-*, etc.), adds the one missing token (--divider), and deletes the .wp-scope blocks + class. Visually identical (aliases resolve to the same values); the preview now inherits the same design tokens as the shared views and the rest of the app instead of a hand-rolled parallel set.

* refactor(docs): adopt emcn Badge + dedup resolveIcon in workflow-preview

output-bundle's hand-rolled type badge (BADGE_COLORS + a styled span) becomes the emcn Badge (its green/blue/orange/purple/gray variants use the identical --badge-* tokens). resolveIcon, which had three copies, is now imported once from block-icons by output-bundle and block-inspector.

* refactor(docs): rebuild the preview inspector on emcn chip primitives

The lightbox inspector was a hand-rolled facsimile (raw divs + a CONTROL class string + inline dashed borders). It now composes from the same @sim/emcn primitives the live editor's sub-block controls wrap — ChipSelect/ChipInput/ChipTextarea(viewOnly)/ChipSwitch/ChipTag/FieldDivider/Label — so it reads as the real editor panel, fed example data (read-only, full opacity via readOnly/viewOnly, not greyed). Slider stays minimal (no emcn equivalent) but on app tokens. Props API and embedded/standalone modes unchanged.

* refactor(docs): render the block-reference hero through the shared View

Retires the hand-rolled BlockCard (a parallel reimplementation of WorkflowBlockView) and the BlockDisplaySpec data model. Each block hero is now a single-block PreviewWorkflow (block-display-workflows.ts) rendered through the same toReactFlowElements -> DocsBlockNode -> WorkflowBlockView pipeline as the diagrams, mounted in a minimal fitView ReactFlow (maxZoom 1.3, no canvas chrome). A single block can no longer drift from the canvas.

* fix(docs): define sim's type scale + align the preview inspector to the editor

Docs Tailwind v4 never defined sim's custom font sizes (text-small/caption/md/micro), so emcn components (Label, Badge, the shared views) fell back to inherited sizes — the inspector labels rendered huge. Adds the type scale to the docs @theme. Also aligns the inspector header to the real editor panel (surface-4 bar, size-[18px] rounded-sm icon, text-sm name) and removes the Connections section (and its now-dead prop/wiring).

* fix(docs): inspector shows the full field list + dragged positions persist

Inspector: shows the block type's full field list (from the reference data) with the example's values overlaid, so it reads like the editor panel instead of only the canvas summary rows. Drag: selecting another block no longer relayouts the canvas — node positions the viewer dragged are preserved across highlight/selection changes (only a different workflow relayouts).

* feat(docs): highlight <> references + env vars; hide Ask AI over the lightbox; respace blocks

Inspector text fields render the value with <...> block references and {{...}} environment variables highlighted in brand-secondary (a lean read-only port of the editor's formatDisplayText), in the canonical chip field chrome. The floating Ask AI widget is hidden while a preview lightbox is open. Plus the example-data respacing so the editor-faithful Error row no longer makes stacked blocks overlap.

* fix(docs): make per-type field templates match the real block registry

Audited every block type's field list (the source the inspector + block-reference heroes render) against apps/sim/blocks/blocks/*. Corrected drift to the registry's default-visible fields, titles, and order: agent gains Temperature; router gains Model; wait gains Async; schedule rewritten (default is Daily, not minutes); webhook_trigger expanded to its real default-visible set; human_in_the_loop notification title fixed. Provider-credential and advanced-mode fields stay hidden, matching the editor. Canvas diagrams keep their clean curated rows; the inspector now shows the full, real field list per the chosen clean-canvas/full-inspector split.

* improvement(docs): taller default preview height so respaced diagrams aren't shrunk

Bumps the default WorkflowPreview height 260->300 (the respaced, editor-faithful blocks are taller, so fitView was shrinking diagrams that relied on the default). The tall how-it-runs routing diagram gets 400.

* improvement(docs): zoomable inline preview + taller default + themed controls

The inline preview is now zoomable outside the lightbox: adds react-flow zoom/fit Controls (themed to the dark canvas chrome) and enables pinch-zoom, while keeping scroll-zoom off so the page still scrolls over the diagram. Pan-drag and click-block-to-inspect already worked. Default height 300->340.

* improvement(docs): click canvas to expand; click empty lightbox to deselect

Clicking the inline preview canvas opens the full lightbox; clicking empty space in the lightbox clears the selection, matching the real editor.

* improvement(docs): reveal inline zoom controls on hover only

The always-visible zoom controls felt heavy on the inline preview; they now fade in on hover (matching the expand button) and stay visible in the lightbox.

* improvement(docs): drop zoom controls on the inline preview

Inline preview keeps pinch-zoom, pan, drag, and click-to-expand; zoom buttons stay in the lightbox only.

* improvement(docs): remove zoom controls from the lightbox too

Both previews zoom via scroll/pinch and pan via drag; no on-canvas zoom buttons. Drops the Controls import and its theming CSS.

* improvement(docs): match the real canvas — flat background + editor edge geometry

Closes the last faithfulness gaps the audit found: removes the dot grid (the real editor hides its background — flat bg), aligns PreviewEdge to the editor's smoothstep math (borderRadius 8, offset 30) and 2px stroke (default + error edges), the selection ring to 1.75px, and minZoom to 0.1. Structural parity (blocks/handles/containers/colors/tokens) was already shared. Kept PreviewEdge rather than swapping to WorkflowEdgeView, which would clobber the docs-only highlight/dim/animate for no visual gain.

* improvement(docs): rebrand the docs assistant as 'Ask Sim', styled like the real chat input

Renames the floating assistant from 'Ask AI' to 'Ask Sim' (matching the platform's voice — you talk to Sim) and restyles the composer to mirror the home chat input: a rounded-2xl bordered field with the toolbar inside, and the same 28px circular send/stop button (the home's exact active/disabled colors + white/black arrow). Updates the lightbox hide-selector to the new label.

* improvement(docs): match Ask Sim message styling to the mothership chat

Aligns the user bubble (rounded-[16px] surface-5, text-base/primary, leading-23, max-w-85%) and the assistant markdown (text-base, 600 headings/strong, text-primary dashed-underline links, surface-5 code blocks) to the real mothership chat's user-message + chat-content treatment, instead of the prior generic text-sm rendering. The composer already mirrors the home user-input (rounded-2xl field + 28px circular send button).

* improvement(docs): compact single-row Ask Sim composer

The two-row layout left a tall dead gap (the docs widget has no toolbar buttons to fill the second row). The composer is now a single row — textarea with the circular send button inline — so it sits at the natural input height.

* fix(docs): pass the router Context value to the shared view

DocsBlockNode never set routerContextValue, so the view (which renders the router's Context row from that prop, not from rows) showed a blank Context even when the preview data authored a value like <start.input>. Extract it from the block's Context row and pass it through.

* fix(docs): don't apply a block-type field template that doesn't match the block

inspectorFieldsFor keyed the full field template purely off block.type, but some types are reused across roles (a table action block vs the table trigger, a webhook trigger vs the webhook action), so the wrong template was applied. Only use the template when the block's authored rows are actually a subset of it; otherwise fall back to the block's own rows.

* fix(docs): connect preview edges to subflow container handles

toReactFlowElements hardcoded targetHandle to 'target' and defaulted source handles to 'source', but Loop/Parallel containers (SubflowNodeView) expose a 'loop-end-source'/'parallel-end-source' output handle and a left input handle with no id. Edges into and out of containers therefore failed to connect. Resolve each edge end to the block's real handle based on whether it's a container.

* fix(docs): don't expand the inspector template for blocks with no rows

block.rows.every(...) is vacuously true for an empty rows array, so a block defined only by branches (e.g. a router in ROUTING_WORKFLOW) inherited the type template's invented field defaults. Require non-empty authored rows before applying the template.

* fix(docs): render blank branch/router-context values as '-' like the editor

The editor maps condition/router branch values and the router Context through getDisplayValue, which renders '-' for a blank value. DocsBlockNode mapped them to an empty string, so else branches and unset routes looked blank instead of matching the editor. Mirror getDisplayValue's empty-value handling.

* fix(docs): show '-' for blank inspector branch values, matching the canvas

inspectorFieldsFor passed raw branch.value into the lightbox branch fields, so an unset else route read blank in the inspector while DocsBlockNode (and the editor's getDisplayValue) render '-' on the canvas. Normalize the same way; drop the now-redundant placeholder.
2026-06-29 20:27:33 -07:00
Waleed bcf6a804f9 improvement(emcn): extract design system into shared @sim/emcn package (#5257)
Moves apps/sim/components/emcn into a shared @sim/emcn package consumed directly by apps/sim and apps/docs. cn/keyboard/use-copy-to-clipboard move into the package; all imports become direct @sim/emcn (icons via @sim/emcn/icons, CSS via file path). ChipModal email validation is now prop-driven (quickValidateEmail stays in apps/sim, injected via validate). Docs drops its local chip/chip-dropdown/dropdown-menu mirrors and consumes @sim/emcn.
2026-06-28 22:50:48 -07:00
Will ChenandClaude Opus 4.8 6355c8e699 improvement(docs): Ask AI chat grounded in the docs vector store (#5172)
* docs: Ask AI chat grounded in the docs vector store

Adds an Ask AI chat to the docs site. A floating launcher opens a chat panel
backed by the Vercel AI SDK (OpenAI provider, OPENAI_API_KEY from the
environment). A searchDocs tool runs locale-scoped vector/keyword search over
the existing docs embeddings so answers cite real pages.

The public endpoint is hardened: per-request size/token/step caps, message
sanitization (no client-injected tool results or system prompts), origin
checks, and a per-IP rate limit. Non-English retrieval uses keyword search;
English vector search applies a similarity threshold.

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

* docs: harden Ask AI retrieval + fix stale loading state

- searchDocs: wrap the keyword query in try/catch too, so each retrieval path
  (keyword, vector) is independent best-effort
- ask-ai: gate the loading ellipsis to the in-progress (last) message so older
  empty bubbles don't re-show it while a later request streams

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:32:29 -07:00
Waleed 7d46103d09 chore(deps): remove unused dependencies and harden CI supply chain (#5119)
* chore(deps): remove unused dependencies and harden CI supply chain

Dependency cleanup:
- Remove unused deps: papaparse, unified, and 6 unused Radix primitives
  (alert-dialog, radio-group, scroll-area, separator, toggle, visually-hidden)
  plus @tanstack/react-query-devtools (all verified zero imports repo-wide)
- Consolidate jwt-decode into the existing jose dependency (decodeJwt)
- Migrate react-window to @tanstack/react-virtual to drop a redundant
  virtualization library (terminal, structured-output, code viewer)
- Remove the better-auth-harmony plugin and its gating env flag

Supply-chain hardening:
- SHA-pin every GitHub Action to a full commit SHA with a version comment
- Pin CI bun-version to 1.3.13 (was "latest" in the release job)
- Raise bun minimumReleaseAge cooldown from 3 to 7 days
- Add a non-blocking `bun audit` step in CI
- Add a CODEOWNERS gate routing dependency-manifest changes to @simstudioai/deps

* chore(deps): remove unused apps/docs dependencies (@tabler/icons-react, dotenv-cli)

* style(search-modal): use Send icon for Invite teammates action

* feat(search-modal): surface New chat as the top action above Create workflow

* feat(search-modal): add Secrets to the pages list
2026-06-17 14:56:43 -07:00
Will ChenandClaude Opus 4.8 bc55fc3b50 improvement(docs): builder-first IA reorganization of the English docs (#4896)
* docs: reorganize into topic/ontology IA with a builder-first rewrite

Restructure the English docs from internal product categories into a
topic-based information architecture, and rewrite the conceptual pages
to install a mental model first rather than enumerate features.

Structure & navigation
- Reorder the sidebar to follow how someone builds: Get Started ->
  Workflows -> Tables -> Files -> Knowledge Bases -> Logs ->
  Building agents -> Mothership -> Workspaces -> Platform -> Reference.
- Demote the generated blocks/tools/triggers catalogs to a Reference
  section at the bottom.
- Break up the monolithic execution/ folder into deployment/ and
  logs-debugging/; collapse connections/* and variables/* into single
  pages under workflows/.
- Rename capabilities/ to building-agents/; relabel the integration
  catalog as "Integrations". Remove deprecated copilot and form
  deployment. Redirects added in next.config.ts for every moved URL.

Conceptual rewrites
- Workflows core (index, how-it-runs, data-flow, connections,
  variables): one mental model, one running example, terser prose.
- New building-agents overview distinguishes an agent (a workflow you
  build) from an Agent block (one reasoning step), plus a "choosing
  what to use" guide.
- Concept-trim passes on Knowledge Base, Tables, Blocks, Triggers
  overviews; new task pages for KB, Tables, and Files.
- New code-verified Alerts page.

Infrastructure
- pageType frontmatter (concept/guide/reference) + badge render.
- WorkflowPreview / OutputBundle components to embed real, app-styled
  workflow diagrams (adds framer-motion + reactflow to apps/docs).

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

* feat(docs): spec-driven BlockPreview for block reference heroes

Replace the static screenshot hero on each block reference page with a
<BlockPreview> that renders the block exactly as the builder canvas shows
it — header icon, sub-block rows, and branch/error handles — from a
hand-authored display spec. Static and non-interactive (no ReactFlow), so
it can't be panned or dragged, and self-updating to edit.

- block-display-specs.ts: one editable spec per block (rows, branches, handles)
- block-preview.tsx: static scaled card renderer with decorative handles
- block-icons.tsx: brand glyphs for the core block types; icons.tsx adds WaitIcon
- 14 block + 3 trigger pages swapped from <Image> to <BlockPreview>

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

* fix(docs): correct stale navigation and removed-feature references

Audited the docs against the product changelog (GitHub releases / staging
git history) for content that misleads readers — features that moved, were
renamed, or removed — rather than cosmetic drift. Fixes:

- Skills: no longer a Settings tab. It was promoted to its own workspace
  page (#4354), so "Settings → Skills under the Tools section" sent readers
  to a tab that no longer exists. (skills/index.mdx)
- Env vars: the workspace tab is "Secrets", not "Environment Variables"
  (credentials→secrets rename, #4364). (quick-reference/index.mdx)
- Mothership FAQ pointed to "Settings → Credentials" for integration
  connections; integrations moved to their own page and there is no
  Credentials tab. (mothership/tasks.mdx)
- Vision block was retired (#4684); a tip still named it. Reworded to
  "an Agent using a vision-capable model". (files/passing-files.mdx)
- Getting-started FAQ told new users to "use the Copilot feature" to build
  in natural language — that surface is Mothership. (getting-started)
- Removed the dead "Mod+Y → Go to templates" shortcut; the templates
  gallery was removed (#4354). (keyboard-shortcuts)

Note: MCP "tools" (Settings → Tools, for consuming) and MCP "servers"
(Settings → System, for exposing) are distinct surfaces — both doc
references are correct and were intentionally left as-is.

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

* fix(docs): repair broken /docs-prefixed enterprise links

The enterprise overview linked to /docs/enterprise/* (access-control, sso,
whitelabeling, audit-logs, data-retention, data-drains), but the docs site
is served at root — those 6 links 404'd. Now root-relative /enterprise/*.

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

* fix(docs): refresh stale workflow-preview example blocks

The /workflows diagram blocks are hand-authored (separate from the
spec-driven BlockPreview heroes) and had drifted from the real UI:
- Agent color purple #6f3dfa -> green #33C482 (the var(--brand) rebrand)
- Model gpt-4o -> claude-sonnet-4-6 (current default)
- "Prompt" row -> "Messages" (the actual agent sub-block)
- Start color #34B5FF -> #2FB3FF (real starter bgColor)

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

* fix(docs): align BlockPreview input/output handles to the card edge

The header (input/output) handles are positioned relative to the card and
used a -16px offset, so they floated 8px past the edge. Row/error handles
are -16px relative to a row that's already inset 8px by content padding, so
they sit correctly. Header handles are now -8px, so every handle sticks out
the same 8px and hugs the block edge.

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

* docs(blocks): rewrite Agent reference to match the current block

The page documented the old UI (System/User Prompt, no Files or Skills, Memory
taught as a separate block — contradicting its own FAQ). Rewritten to the real
sub-blocks (Messages, Model, Files, Tools, Skills, Memory, Response Format) in
the builder voice of the workflows exemplars: oriented opening, agent vs
Agent-block callout, outputs table, a live WorkflowPreview example, FAQ kept and
corrected (tool control "Force", not "Required"). pageType: reference.

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

* docs(blocks): rewrite API reference to match the current block

Tightened to the builder voice and the real config (URL, Method, Query Params,
Headers, Body + Advanced timeout/retries/backoff). Dropped the off-topic
"Dynamic URL Construction" / "Response Validation" sections (those are
Function-block techniques, not API config). Outputs table, FAQ kept. The example
is now a live WorkflowPreview (new API_FETCH_WORKFLOW in examples.ts, exported
via the barrel). pageType: reference.

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

* docs(blocks): rewrite Condition reference to match the current block

Tightened to the builder voice: oriented opening (branches on boolean
expressions, no model call, vs Router), the real branch model (if / else if /
else, checked top to bottom), connection-tag expression examples, an error-path
callout, outputs table, and a live branching WorkflowPreview example
(CONDITION_ROUTE_WORKFLOW). FAQ kept. pageType: reference.

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

* docs(blocks): restore Best Practices + multi-example workflows on Condition

Recalibration: reference pages keep genuine substance (Best Practices, every
distinct example), cutting only redundancy and verbose register. Restores the
Best Practices section and turns the three use cases into three rendered
WorkflowPreview examples (route by priority, moderate content, branch
onboarding). Adds CONDITION_MODERATE_WORKFLOW and CONDITION_ONBOARD_WORKFLOW.

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

* docs(blocks): restore Best Practices on Agent reference

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

* docs(blocks): restore Best Practices on API reference

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

* docs(blocks): rewrite Function reference to match the current block

Fixed the verbose register and dropped the duplicated outputs section + the
stale Python screenshot/TODO, while keeping the real substance: JS vs Python
(local vs E2B sandbox), the large-inputs sim.files/sim.values helpers, the
worked loyalty-score example, and Best Practices. The use cases are now two
rendered WorkflowPreview examples (reshape an API response, validate input).
Adds FUNCTION_RESHAPE_WORKFLOW and FUNCTION_VALIDATE_WORKFLOW. pageType: reference.

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

* docs(blocks): rewrite Router reference to match the current block

Cleaned the register, generalized the drifting model list, and folded the
Router-vs-Condition guidance into a callout. Kept the substance (routes as
output ports, NO_MATCH error path, all seven outputs, Best Practices, FAQ). The
three same-shape use cases collapse to one rendered triage WorkflowPreview
(ROUTER_TRIAGE_WORKFLOW), which the prose notes stands for the pattern.
pageType: reference.

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

* docs(blocks): restore the classify and lead-qual examples on Router

I wrongly folded two distinct Router scenarios into a note. Restored all three
as their own rendered WorkflowPreview examples: triage a support ticket,
classify feedback (to child workflows), qualify a lead (sales vs self-serve).
Adds ROUTER_CLASSIFY_WORKFLOW and ROUTER_LEAD_WORKFLOW. (Also exports
RESPONSE_API_WORKFLOW for the next page.)

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

* docs(blocks): rewrite Response reference to match the current block

Cleaned the register and broadened "Variable References" to connection tags
(any output, not just workflow variables). Kept the substance: exit-point
semantics, Builder/Editor mode, status codes, headers, the parallel-branch
warning, Best Practices, FAQ. All three use cases are now rendered
WorkflowPreview examples (API endpoint, webhook ack, status-per-branch). Adds
RESPONSE_API/WEBHOOK/ERROR_WORKFLOW. pageType: reference.

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

* docs(blocks): rewrite Variables reference to match the current block

Cleaned the register, corrected the outputs (each assignment is also exposed as
<variables.name>, not "no outputs"), and kept the substance: assignments
reference earlier outputs and current values, global <variable.name> access,
Best Practices, FAQ. Two use cases now render as WorkflowPreview examples (count
retries, hold config). Adds VARIABLES_RETRY_WORKFLOW and VARIABLES_CONFIG_WORKFLOW.
pageType: reference.

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

* docs(blocks): rewrite Wait reference to match the current block

Corrected a real staleness: the block now has an Async mode that suspends the
run for minutes/hours/days (not a hard 10-minute cap), plus a resumeAt output.
Documents Wait Amount / Unit / Async, the sync-vs-async distinction, all three
outputs, Best Practices, and updated FAQ. Two rendered WorkflowPreview examples
(space out API calls, delayed follow-up). Adds WAIT_RATELIMIT_WORKFLOW and
WAIT_FOLLOWUP_WORKFLOW. pageType: reference.

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

* docs(blocks): polish Credential reference (frontmatter, fold redundant tabs)

The page was already accurate to the block (Select/List operations, the outputs
tabs, the wiring steps). Light touch only: added description + pageType, made the
header consistent, and folded the two identical Gmail/Slack "how to wire" tabs
into one line. Examples stay as labeled flows + the List/ForEach screenshot,
since they use integration blocks and a Loop the WorkflowPreview can't render.

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

* docs(blocks): render the shared-credential example + icon fallback for integrations

Addressing the gap: WorkflowPreview block nodes now fall back to the integration
icon map, so diagrams can show Gmail/Drive/Slack/etc. with their real glyphs, not
just core blocks. Renders the Credential "share one account across blocks" example
as a WorkflowPreview (CREDENTIAL_SHARE_WORKFLOW). The multi-account and
List+ForEach examples stay as labeled flows + screenshot (the latter uses a Loop
container the preview can't render). Also exports EVALUATOR_GATE_WORKFLOW for the
next page.

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

* docs(blocks): rewrite Evaluator reference to match the current block

Cleaned the register, generalized the drifting model list, and documented the
per-metric outputs (<evaluator.metricname>), which the page omitted. Kept the
substance (metrics with name/description/range, structured-output guarantee,
Best Practices, FAQ). The quality-gate example renders as a WorkflowPreview;
the same shape covers the parallel-variations and support-QC patterns, noted in
prose. pageType: reference.

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

* docs(blocks): render the Credential route-by-logic example too

The icon fallback unblocked it: the "route to a different account by logic"
example now renders as a WorkflowPreview (CREDENTIAL_ROUTE_WORKFLOW), a Condition
selecting a production vs staging credential. The List + ForEach example stays a
screenshot because it nests blocks in a Loop container the flat WorkflowPreview
can't represent.

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

* docs(blocks): render Guardrails examples + light accuracy pass

Kept the full substance (four validation types, PII entity/language detail,
the PII screenshot and video, outputs, Best Practices, FAQ). Light fixes:
frontmatter, and generalized the drifting model names (GPT-4o / Claude 3.7) to
"a strong reasoning model" with the current default. The three use cases now
render as WorkflowPreview examples (validate JSON, check grounding, block PII).
Adds GUARDRAILS_JSON/HALLUCINATION/PII_WORKFLOW. pageType: reference.

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

* docs(blocks): render Human-in-the-Loop examples + frontmatter

Kept all the substance (Display Data, Notification, Resume Form, the Approval
Methods and API Execute Behavior tabs, outputs, the paused/resume example).
Added frontmatter and rendered the use cases as WorkflowPreview examples
(approve before publish, two-stage approval, verify extracted data); Quality
Control folds into the approval note as the same approve-then-act shape. Adds
HITL_APPROVAL/MULTISTAGE/VALIDATE_WORKFLOW. pageType: reference.

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

* docs(blocks): render Webhook examples + frontmatter

The page was already accurate (Webhook URL/Payload/Signing Secret/Headers, the
automatic-headers table, HMAC details, outputs, POST-only callout, FAQ). Added
frontmatter and rendered the two use cases as WorkflowPreview examples (notify a
service, fire on a check). Adds WEBHOOK_NOTIFY_WORKFLOW and
WEBHOOK_TRIGGER_WORKFLOW. pageType: reference.

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

* docs(blocks): add example + pageType to Workflow block reference

The page was already accurate and well-structured (Configure It, outputs,
deployment-status badge, execution notes, FAQ). Added pageType: reference and a
rendered WorkflowPreview example showing a parent calling the child workflow
enrich-lead and reading its result. Adds WORKFLOW_CALL_WORKFLOW.

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

* docs(blocks): container rendering for Loop/Parallel + render the Loop example

Adds subflow/container support to WorkflowPreview, modeled on the app's
subflow-node.tsx: a solid-bordered box with a header (icon + name), an internal
"Start" pill whose handle feeds the first nested block, and target/source
handles at the vertical center. PreviewBlock gains size/parentId; edges gain an
optional sourceHandle; nodes render nested children via React Flow parentNode.
Renders the Loop reference's ForEach example (LOOP_WORKFLOW) and keeps the four
loop-type sections + inside/outside referencing + caps. pageType: reference.

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

* docs(blocks): fix the Loop container's Start-pill connector

The Start pill -> first-block edge wasn't rendering: it was a React Flow
parent->child edge (unreliable), and the opaque container body hid it. Nested
blocks now render as absolute-positioned top-level nodes (container below at
zIndex 0, blocks above at zIndex 1), so the connector is an ordinary edge, and
the container body is see-through so it's visible.

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

* docs(blocks): render the Parallel example + frontmatter (last core block)

Reuses the container rendering for the Parallel reference. Kept all substance
(count/collection types, inside/outside referencing, batch size of 20, instance
isolation, the Parallel-vs-Loop table, Best Practices, FAQ). Added frontmatter
and a rendered container WorkflowPreview (PARALLEL_WORKFLOW: distribute tasks,
call concurrently, aggregate <parallel.results>); the two use cases stay as
labeled flows. Adds PARALLEL_WORKFLOW. pageType: reference.

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

* docs(blocks): prose glow-up for Guardrails to match the agent/condition voice

Rewrote the listy register (**Use Cases:** / **How It Works:** / **Configuration:**
scaffolding, "Use this when you need to..." filler) into the plain builder voice,
matching the depth of the Agent/Condition/Function rewrites. Kept every
validation type, option, range, the full PII entity/region list, the screenshot
and video, the outputs table, the rendered examples, Best Practices, and FAQ.

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

* docs(blocks): prose glow-up for Loop to match the agent/condition voice

Rewrote into the plain builder voice and cut the filler: dropped the "Use this
when you need to..." lines and the ASCII "Example: Iteration 1, 2, 3" pseudo-code,
and folded the duplicated Inputs/Outputs tabs into Configuration + Referencing
sections. Kept all four loop types with their screenshots, the inside/outside
reference rules, the 1,000-iteration cap, sequential-vs-parallel guidance, the
rendered example, and FAQ.

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

* docs(blocks): prose glow-up for Parallel to match the agent/condition voice

Same treatment as Loop: plain builder voice, dropped the ASCII pseudo-code and
the duplicated Inputs/Outputs tabs, folded the verbose Advanced Features into
tight Configuration + Referencing sections. Kept both types with screenshots,
the batch-size-of-20 cap, instance isolation, large-result indexing, the
Parallel-vs-Loop table, the rendered example, and FAQ.

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

* docs(blocks): prose glow-up for Human-in-the-Loop

Tightened the register: folded the pause sentence into the intro, made the
section headers consistent (Configuration, Outputs), converted the bold-list
Block Outputs into a table, condensed the Notification channel bullets to a
line, and renamed the second "Example" so it no longer collides with the
rendered Examples. Kept all the substance — Display Data / Notification / Resume
Form, the Approval Methods and API Execute Behavior tabs, the portal video, and FAQ.

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

* docs(blocks): re-enrich Loop prose (fuller, explanatory — not terse)

The first glow-up overcorrected into terse fragments. Restored proper
docs-quality prose at the Agent/Condition level: each loop type now explains
what it does, when to use it, and the relevant reference; Configuration,
Referencing, nesting, and Best Practices give context and the "why," not just
bullets. Same substance, readable depth.

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

* docs(blocks): re-balance Parallel prose to the Agent/Condition register

Calibrated to the level signed off on elsewhere: each concept explained in a
couple of clear sentences with a concrete detail — informative, not terse, not
padded. Kept both types with screenshots, batch-size cap, isolation, large-result
indexing, the Parallel-vs-Loop table, the rendered example, and FAQ.

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

* docs(blocks): restore the Notification channel detail on HITL

The glow-up over-compressed: it flattened the five notification channels (each
with what they do) into one sentence. Restored them as a list in plain voice —
tightening register shouldn't drop genuinely useful reference detail.

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

* docs(blocks): builder-voice polish on the Credential intro

Light touch only — the page was already well-structured and explanatory, so just
led the intro with what the block does (and bolded the name) to match the other
references. No content changed elsewhere.

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

* docs(triggers): rewrite Start trigger in the builder voice

Tightened the register, swapped the <code>&lt;&gt;</code> noise for backticks,
added pageType + an outputs table, and kept all substance: Input Format types,
chat-only outputs (input/conversationId/files), the editor/API/chat tabs, and
best practices.

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

* docs(triggers): rewrite Schedule trigger in the builder voice

Plain voice and clean markdown (dropped the raw <ul>/<div> lists). Kept all
substance: simple intervals, cron examples, timezone, deploy-tied activation,
the 100-failure auto-disable, and FAQ. Added pageType.

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

* docs(triggers): refocus Webhook trigger on the generic (native) trigger

Rewrote in the builder voice and separated out the integration content: the
page now documents the generic Webhook trigger (URL, Input Format, auth, custom
response, outputs, dedup/rate-limit/deploy/no-auto-disable). The "trigger mode
for service blocks" section is reduced to a short pointer + the demo video, and
the long supported-services catalog and vague use-case bullets are dropped in
favor of the Triggers index. Fixed the title (Webhook) and added pageType.

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

* docs(triggers): builder-voice glow-up for RSS

Light pass: added pageType + description, tightened the intro, and presented the
output fields as an <rss.*> outputs table. Kept the polling config, use cases,
the published-after-save callout, and the FAQ (poll cadence, dedup, 25-item cap,
auto-disable, Atom support).

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

* docs(triggers): rewrite Table trigger off the auto-generated card

Replaced the BlockInfoCard/'provides 1 trigger' auto-gen format with a real
builder-voice page: a spec-driven BlockPreview hero (added a 'table' spec),
plain-language Configuration (table, event type, watch columns, include
headers), and a full <table.*> outputs table. pageType: reference.

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

* docs(triggers): frame the index around native triggers + separate the catalog

Reframed "generic" as native (no connected account) and promoted RSS and Table
into the native set alongside Start/Schedule/Webhook — cards, comparison table,
and integration paragraph updated to match. In the sidebar, grouped the five
native triggers under a "Native triggers" header and divided the ~44 service
triggers under "Integration triggers" (nav-only — no files moved, URLs stable;
the move to integrations/ is a later, separate change).

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

* docs: promote Core Blocks + Core Triggers into the Workflows area

Restructured the Documentation sidebar (meta-only — no files moved, URLs stable):
after Deployment, the 16 core block pages now live under a "Core Blocks" section
and the 5 native trigger pages under "Core Triggers", instead of buried in the
bottom Reference catalog. Removed the now-redundant blocks tree from Reference,
and retitled the Reference triggers tree "Integration triggers" so it holds just
the service catalog (the native ones are promoted up top).

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

* docs: merge block/trigger overviews into the Workflows overview; Core accordions

Restructured the sidebar and overview hub (meta + content only, no integration
files moved):

- Folded the /blocks and /triggers overview pages into /workflows: the overview
  now carries the core-block catalog (do work / direct flow / shape run), the
  Integrations-and-triggers families framing, the native + integration trigger
  framing, the trigger comparison, manual-run priority, and email-polling groups.
  Deleted blocks/index.mdx and triggers/index.mdx as redundant.
- Promoted the 16 core blocks into a "Core Blocks" folder accordion and the
  native triggers into a "Core Triggers" accordion, both under Workflows after
  Deployment. Integration triggers stay inside Core Triggers under a labeled
  divider, temporary until they move to integrations/<service> (tabs) later.
- Repointed every /blocks and /triggers index link to the /workflows#blocks and
  /workflows#triggers sections.

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

* docs: split integration triggers into their own Reference accordion

Core Triggers is now the 5 native triggers only. Moved the 43 service triggers
out of triggers/ into a new integration-triggers/ folder, surfaced as an
"Integration triggers" accordion under Reference (an accordion must be its own
folder in Fumadocs). In Workflows, Core Triggers now sits before Core Blocks.
URLs: /triggers/<service> -> /integration-triggers/<service> (native /triggers/*
unchanged); the integrations/<service> tabbed-page migration remains the later step.

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

* docs(workflows): trim the overview back to an introduction

It had drifted from a concept intro into a catalog. Kept the spine (the four
parts with their previews, how-it-runs, workflows-in-context) and compressed the
merged-in material: the full 16-block enumeration becomes a three-kind taxonomy
with examples, the trigger section a short native/integration framing. Cut the
anxious in-between — manual-run trigger priority, the niche email-polling-groups
feature (belongs on the Gmail/Outlook trigger pages), the redundant block-def
line, the Start-outputs callout half, the connections video, and the catalog-y
FAQ items. Dropped the unused Video import.

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

* docs: relocate email-polling + trigger-priority out of the overview

Moved the two bits cut from the workflows overview to durable, generator-safe
homes: email-polling groups -> the Integrations (connecting accounts) page;
manual-run trigger priority -> the Start trigger page. Also added 'table' to the
generator's HANDWRITTEN_TRIGGER_DOCS / SKIP_TRIGGER_PROVIDERS so the hand-written
Table trigger page is no longer overwritten by generate-docs.

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

* feat(docs-gen): emit per-service integration pages (actions + Trigger section)

Rewrites the generator to output one page per service under integrations/
instead of split tools/ + triggers/. Block pass writes the service's actions;
trigger pass appends a '## Triggers' section (badged) to the same page, or writes
a standalone page for trigger-only services. Meta is written after both passes;
hand-written integration pages are preserved; docsUrl repointed to /integrations.

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

* feat(docs): unify tools + triggers into per-service /integrations pages

Encodes the ontology "everything is a block; some blocks are triggers." The
generator now emits one page per service under integrations/ — the service's
Actions plus, when it has one, a Triggers section on the same page — replacing
the split tools/<service> + triggers/<service>. No "Tools" terminology.

- generate-docs.ts: output to integrations/, merge trigger sections into each
  service page (standalone for trigger-only services), Actions heading, table
  block now generated, docsUrl -> /integrations, hand-written pages preserved.
- Nuked tools/ (213) and the interim integration-triggers/ (43); moved the
  custom-tools guide to building-agents/; knowledge/memory/file/table links and
  meta repointed to /integrations.
- Sidebar: integrations catalog now under Reference (was tools); removed the
  Workspaces integrations entry and the integration-triggers tree.
- block-icons: wait uses lucide Clock (the generated icons.tsx no longer carries
  a hand-added WaitIcon). Landing integrations data regenerated.

No redirects (fresh start). Native Core Blocks/Core Triggers unchanged.

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

* fix(docs): recover the hand-written manual-content intros on integration pages

The tools->integrations relocation generated fresh pages, so the generator never
saw the old tools/<service>.mdx to preserve its {/* MANUAL-CONTENT */} sections —
198 curated intros (AgentMail, etc.) were dropped. Reseeded each integrations
page from the pre-move tools page in git, re-ran the generator (which now merges
the manual intro into the new Actions/Triggers format), and repointed /tools/
links inside the recovered prose.

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

* docs(scripts): rewrite the generator README for the integrations model

Brings scripts/README.md current: integration pages are derived from the
apps/sim block/tool/trigger registry (canonical-sources map), the golden rule
not to hand-edit generated pages, the MANUAL-CONTENT escape hatch, which pages
are hand-written/skipped, and the icons.tsx-overwrite gotcha.

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

* docs: regenerate integration docs from staging-synced apps/sim

After merging staging, regenerated so the integration pages reflect current
source: correct block colors/configs (e.g. Gmail #FFFFFF), the new integrations
(sendblue, millionverifier, neverbounce, zerobounce), and staging's icon set.
Pages for integrations staging hid are removed; manual-content intros preserved.

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

* fix(docs-gen): don't let stale-doc cleanup delete hand-written integration pages

Staging's cleanupStaleToolDocs removes any integrations/*.mdx that isn't a visible
tools block — it only guarded `index`, so it deleted the hand-written
google/atlassian service-account pages. Now guards all HANDWRITTEN_INTEGRATION_DOCS.
Restored the two pages, and repointed /integrations/file links to /files (staging
hides the file block, so it has no integration page).

Note: staging recategorized a2a/mysql/postgresql tools -> 'blocks' (and hid file),
so they correctly drop out of the integration catalog and are currently
undocumented — an IA decision to revisit.

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

* fix(docs-gen): stop cleanup/writer filter mismatch from eating manual content

Comprehensive-review findings, all generator-consistency bugs:
- cleanup used staging's isIntegrationBlock while the writer kept the legacy
  filter, so integrations/{knowledge,memory,table}.mdx were deleted then
  regenerated without their manual intros every run. Both now honor a shared
  NATIVE_RESOURCE_BLOCK_TYPES set; intros reseeded.
- Trigger-only services (imap, circleback; category 'triggers') were likewise
  deleted each run; the canonical set now includes visible trigger-category
  blocks, the standalone writer preserves manual content, and their intros are
  reseeded.
- Mapped jsm -> jira_service_management, so JSM triggers merge into the JSM
  integration page instead of an orphan jsm.mdx (removed).
- Repointed lingering bare /tools links to /integrations; added missing
  pageType to integrations/index and building-agents/custom-tools.
Double-regen is now churn-free (idempotent) with all manual content intact.

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

* fix(docs): recover staging's enriched Table doc + never drop manual content

The merge resolution deleted staging's relocated blocks/table.mdx, which carried
substantial enrichment our integrations/table.mdx (reseeded from the older
tools/ version) lacked: Creating Tables (column types/constraints), Filter
Operators, Combining Filters, Sort Specification, Built-in Columns, Limits, and
Notes. Recomposed integrations/table.mdx with that content — Creating Tables
inside the intro manual section, the reference tail in a notes manual section.

Generator fix uncovered en route: a manual section whose insertion anchor is
missing in the generated markdown (e.g. notes with no "## Notes" heading) was
silently dropped on regen. Unplaceable sections now append at the end instead —
manual content is never lost. Verified idempotent across double regeneration.

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

* docs(workspaces): de-philosophize the fundamentals prose

Rewrote in the plain register of the workflows overview: 'draws the boundary
for access' / 'Nothing crosses the boundary' / 'follow the same edge' become
direct statements (only members can access it; a workflow in one workspace
cannot read a table in another). '## The boundary' is now '## Access and
isolation'. All substance kept: every resource type, permission levels,
personal/organization/grandfathered kinds, deployments callout, VISUAL markers.

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

* fix(docs): restore #blocks and #triggers anchors on the workflows overview

The editorial trim renamed '## Blocks' -> '## Kinds of blocks' and
'## Triggers' -> '## How a workflow starts', silently breaking the ten
/workflows#blocks and /workflows#triggers anchor links pointed there when the
old index pages were folded in. Pinned the original ids with explicit heading
anchors. Found by the comparative prose review.

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

* docs: restore the genuinely useful reference bits the rewrite dropped

From the comparative prose review, restored in guidance register (no spec
dumps): temperature tiers on Agent (low/middle/high with ranges), loop/parallel
iteration references in the variables syntax-at-a-glance table, and a short
"Test it" section on the Webhook trigger (curl + check the run in Logs). The
fourth flagged loss (tag-resolver mechanics on connections) turned out to be
already covered — name normalization, case-sensitive paths, missing-output
behavior, and value formatting are all on the page; only the internal resolver
precedence chain was dropped, deliberately.

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

* docs(blocks): rework the Agent intro — encyclopedia register

Replaced the flat opening with a denser, factual one (no metaphor): what the
block does, and its centrality stated as fact — 'Most workflows are built
around one or more Agent blocks.' The agent-vs-Agent-block disambiguation moves
from an info callout into a second paragraph on the block's role in building
agents. Dropped the now-unused Callout import.

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

* docs(integrations): add the HubSpot setup guide for the Marketplace listing

Addresses HubSpot Marketplace review item A1: a public, HubSpot-specific setup
guide following their template — what the app does, install + connect through
the current flow (sidebar Integrations page -> HubSpot -> Add to Sim -> connect
dialog -> HubSpot OAuth), with real screenshots of each step and a placeholder
for the scope-approval shot; configure in a workflow (one-click skills/templates
+ the HubSpot block + trigger mode), use, disconnect (with data consequences),
uninstall from the HubSpot side, troubleshooting. Capability wording is by CRM
object rather than scope enumeration, so it stays accurate after the A2 scope
trim. Lives at /integrations/hubspot-setup, guarded as hand-written,
cross-linked from the HubSpot reference page's intro.

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

* docs(integrations): rewrite the Integrations guide for the sidebar flow

Integrations moved out of Settings to a top-level sidebar page. Rewrote the
guide to the current journey: the Integrations page (Connected/Featured/search),
service pages with one-click skills and templates, + Add to Sim -> connect
dialog (display name + permissions) -> provider OAuth. Replaced the four
Settings-era screenshots with current captures (connect dialog illustrated via
HubSpot); block-side screenshots (account selector, manual credential ID) kept;
one VISUAL marker for the connection detail view pending a fresh capture.
Members/roles, credential-ID, reconnect/disconnect, email polling, and FAQ
substance unchanged apart from navigation.

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

* docs: move Building agents directly after Workflows in the sidebar

The agent-building journey follows straight from workflows (blocks, triggers,
deployment) rather than after the tour of every resource type. Tables/Files/
Knowledge Bases/Logs now follow it. Meta-only reorder.

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

* docs: fill the visual slots coverable by existing components

Six VISUAL markers filled with no new captures needed:
- building-agents overview: rendered the minimal lead-scoring agent
  (Start -> Agent with tool chips -> Response, Agent highlighted) as a
  WorkflowPreview (BUILD_AGENT_WORKFLOW)
- files guide: the read -> summarize -> write chain as a WorkflowPreview
  (FILE_SUMMARY_WORKFLOW)
- tables guide: the query -> classify -> write-back roundtrip as a
  WorkflowPreview (TABLE_ROUNDTRIP_WORKFLOW)
- choosing guide: the six-kind comparison grid as a markdown table
- knowledgebase guide: the Knowledge block's output as an OutputBundle
- workspace fundamentals: removed a duplicate nesting-diagram marker

42 -> 39 VISUAL markers remaining (screenshots + designed diagrams).

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

* docs(components): run-inspector OutputBundle + lightbox with block inspector

Two visual-component upgrades, both mirroring the real app:

- OutputBundle is now a miniature of the run inspector: a Logs column (block
  rows with icon chips and durations, source selected) beside the Output panel's
  typed tree — keys with the app's type-badge semantics (string green, number
  blue, object gray, array purple, boolean orange), chevrons, indent guides,
  primitive values. Styling lifted from the terminal's structured-output.
  Dropped the "Read one value by name" footer (the prose teaches the tag).
  The three usages (data-flow, tables, knowledgebase) get real typed trees;
  data-flow's stale purple/gpt-4o example corrected en route.

- WorkflowPreview gains a lightbox + read-only block inspector: clicking a
  block (or the expand control) opens a 92vw/86vh overlay with zoom and pan,
  and a right-hand inspector panel showing the selected block's full
  configuration — canvas rows truncate, the inspector doesn't. Fields render as
  app-style controls (dropdown/textarea/input by heuristic) with dashed
  dividers, tool chips, and a Connections footer computed from the edges.
  Selection rings without dimming (new selectedBlock option in workflow-data).
  Esc/backdrop closes; body scroll locks while open.

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

* docs: regenerate after staging merge — AppConfig joins integrations/

Staging's new AWS AppConfig integration (#4928) generated its docs into the old
tools/ layout; re-homed to integrations/appconfig.mdx (Actions heading, meta
entry) via the generator. tools/ stays deleted.

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

* docs: redirect the retired tools/ and trigger URLs to integrations/

Revises the earlier fresh-start call: /tools/* are ~200 live, indexed URLs
referenced by deployed app versions' docsLink fields and marketplace listings,
so dropping them cold would 404 from the live product. next.config now 308s:
- /tools -> /integrations, /tools/:slug -> /integrations/:slug
  (custom-tools -> building-agents/custom-tools first)
- old /triggers/<service> -> /integrations/<service>, enumerated so the native
  trigger pages keep resolving; provider-slug mappings for jsm and the
  hyphenated Google/Microsoft slugs
- /blocks and /triggers index URLs -> the workflows overview anchors
Verified every class + native passthroughs against the dev server. Spec updated.

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

* docs(getting-started): rewrite — current UI, cut the post-tutorial padding

The last old-guard page. Accuracy: Agent config now uses Messages (System/User
message) instead of the removed System Prompt/User Prompt fields, the default
model instead of GPT-4o, the banned 'no-code' phrasing is gone, the deploy card
points at /deployment, and frontmatter gets description + pageType. Weight: cut
the 'What You've Built' checklist, the 'Key Concepts You Learned' re-teach
section, the duplicate 'Resources' links, the Start-block hand-holding, and ten
dead icon imports; tightened every step preamble. 203 -> 113 lines with the
full 5-step tutorial, videos, and FAQ intact. (Videos still show the old UI
until the re-recording pass.)

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

* docs: de-fluff the Tier-1 heavy pages (logging, mcp, passing-files, permissions)

From the exhaustive fluff audit, keeping all substance:
- logging: merged the duplicated Console/Logs-page structure, snapshot concept
  stated once instead of three times, cut the generic Best Practices, trivial
  tab walkthrough condensed. Frontmatter added.
- mcp: intro + "What is MCP?" generic bullets folded into two sentences, cut
  the Common Use Cases catalog and the verify-your-config Troubleshooting
  checklists, merged the twice-stated Refresh behavior, security kept as one
  real warning.
- passing-files: marketing opener replaced with a factual lead, fixed the stale
  retired-Vision-block reference (now Agent with a vision model), dropped the
  FAQ item that restated the block catalog verbatim.
- permissions: heading-restating intro replaced with the two-layer model, cut
  the three "Perfect for: stakeholders..." persona lines and the generic Best
  Practices section, dropped the FAQ restating the limits table.
- connectors: audit over-flagged it — the categorized support matrix, API-key
  table, and config examples are genuine reference; frontmatter only.

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

* docs: tier-2 fluff trims (costs, enterprise, mailer, skills)

Conservative sweep from the audit, unambiguous cuts only: the costs CYA opener
and formula restatement, the enterprise marketing intro (now a functional
summary), mailer's restated convenience line and chat-upload comparison, and
skills' third restatement of progressive disclosure. Audit flags screened out
as misfires: mothership/tasks (immediate-vs-scheduled are two facts, not a
duplicate), self-hosting telemetry (real sizing data), and the recently
approved credential/HITL/workflow-block/trigger pages.

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

* docs(skills): update to the Skills tab on the Integrations page + document import

Skills moved again — they now live on the Integrations page's Skills tab in the
workspace sidebar (the doc said "Open the Skills page"). Updated the create flow
(+ Add to Sim -> Add Skill dialog) with fresh screenshots of the tab and both
dialog tabs, and documented the previously-missing Import flow: upload a .md
with YAML frontmatter or a .zip containing SKILL.md, fetch from a GitHub URL, or
paste SKILL.md content (verified against the import route/component; name 64 /
description 1024 limits verified against the contract). Noted the curated
skills suggested on integration pages, cross-linked the Skills tab from the
Integrations guide, and refreshed the location FAQ. Mechanics (progressive
disclosure, load_skill, agent-block attachment) unchanged and still accurate.

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

* docs(building-agents): render the lead-scorer running example on choosing

The page narrated its running example through six sections without ever showing
it. Authored LEAD_SCORER_WORKFLOW (Start -> Enrich workflow-as-tool -> Function
reshape -> Agent with Search/Send Email/CRM tool chips -> Google Sheets append)
and rendered it after the intro, with highlightBlock re-renders in the three
sections that map to a node (deterministic block -> the Sheets append, agent
tool -> the Agent, workflow-as-tool -> Enrich) — the same pattern as the
workflows overview.

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

* docs(tables): rewrite workflow columns around the real lead-scoring example

Rebuilt the page on the ai_startup_customers screenshots instead of captioning
them onto the old hypothetical: one running example throughout — Company Domain
fills domain, Company Info reads it into employee_count/description, Lead Score
Enrichment writes lead_score/priority/score_reasoning. Every section now
describes the actual UI: the grid with group headers, per-row run buttons, and
the 21-running toolbar; the Configure workflow panel (picker, column inputs,
output selection, Auto-run, Run after); the Company Info input/output mapping;
Not found cells explained where the screenshot shows them; the cascade section
describes the example itself. All placeholder markers on the page resolved.

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

* docs: regenerate after staging merge — Slack trigger update + file block re-visible

Staging's mothership v0.2 (#4923) expanded the Slack trigger payload
(interactivity, slash commands: event_type, command, action_id/value/actions,
response_url, trigger_id, callback_id, ...) — regenerated so it lands on the
unified integrations/slack page; the old-layout triggers/slack.mdx from
staging's generator was dropped in the merge. The file block is visible again
upstream, so integrations/file.mdx is back in the catalog.

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

* docs(tables): playbook prose pass on workflow columns + restore File block links

Workflow columns, against the docs-writing playbook: killed the banned
'Term — desc' bullets in the Configure list (term + verb form), restored the one
universal analog (spreadsheet macro), fixed the clipped 'On,/Off,' fragments,
replaced an invented <start.companyDomain> tag with the verified description,
and thinned em-dashes to four page-wide with no clustering. Also repointed
[File] block mentions back to /integrations/file now that the page exists again
(FileV5 is visible upstream); the Files-store links stay on /files.

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

* docs(tables): per-row execution inspection on workflow columns

Two new captures: the cell menu (View execution, Re-run cell, row actions) and
the Log Details trace for a single row's run. New 'Inspecting a row's run'
section ties cell values to real, traceable runs; corrected the re-run guidance
now that Re-run cell exists (the page previously said Run all rows was the only
way to retry).

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

* docs(workflows): drop the confusing 'order by hand' sentence

'You never set the order by hand' read wrong (wiring connections is setting it
by hand), and the replacement was over-explanation. The first sentence already
carries it: Sim works out the order from the connections.

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

* docs(workflows): fix the over-claim about independent blocks

'Two blocks that don't depend on each other run at the same time' is wrong —
independent blocks at different depths run at different times. Concurrency
follows from readiness, not independence: blocks whose dependencies have all
finished run together. Reworded to say that, tied to the image's two agents.

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

* docs(workflows): accuracy audit of how-it-runs against the executor

Verified every claim on the page against apps/sim/executor. One claim was
materially false: "a failed block stops its own path but leaves independent
paths running" — in the engine, an unhandled block failure sets the error flag
and stops scheduling entirely (in-flight blocks finish, nothing new starts);
only a connected error port routes the failure and keeps the run alive. Now
says that. Two imprecisions tightened: a join waits for every feeder *that is
going to run* (deactivated-branch feeders don't hold it up, per the
edge-manager cascade), and Loop also repeats while a condition holds. Confirmed
accurate: per-block readiness scheduling (readyQueue + race, not layers),
branch-skip cascade and empty tags, the 25-hop call-chain cap.

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

* docs(logs): real captures on the overview + prose matched to the UI

The logs-debugging overview had six visual placeholders and no visuals. Three
real captures placed: the workspace Logs page as the hero (rows with status,
credits, trigger, duration), Log Details' Trace tab at the blocks section (the
CRM sync run's spans, with a one-line read of where the time went), and the
editor's live run console at the input/output section. Prose corrected to what
the UI shows: cost is in credits, failed runs are badged Error (dropped the
five-state enum the list doesn't display), and the Trace tab is named. The
row-anatomy marker is covered by the hero; the two designed-diagram markers
(debug-loop flowchart, failed-vs-success comparison) remain.

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

* docs(workflows): one reference syntax, named sources — untangle variables vs connection tags

An exhaustive sweep of "connection tag" found the docs asserting both that a
workflow variable is a connection tag (response.mdx used it as the umbrella for
all angle-bracket references) and that it isn't (variables.mdx). Ruled the
narrow definition canonical — a connection tag reads a block's output; the name
follows the connection — and restructured around the real model:

- variables.mdx: new "One syntax, named sources" section states that everything
  in angle brackets is one mechanism whose first segment names the source, with
  the load-bearing fact stated plainly: `variable` is literal, a connection tag
  starts with the block's own name. The syntax table drops the redundant
  dot-notation row, gets one row per source, and is ordered by resolution
  precedence with the order explained beneath it (absorbing the old Name
  conflicts section). The credentials pointer folds into the env-var section;
  trimmed the "never appears in outputs" overclaim.
- response.mdx: no longer calls a workflow variable a connection tag.
- connections.mdx: the owner page closes the loop — same syntax also reads
  variables and loop/parallel context; a connection tag is the block-output case.

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

* docs(workflows): verify the reference model against the resolver; fix one imprecision

Checked every claim in the new 'One syntax, named sources' section against
apps/sim/executor/variables: resolver chain order is Loop -> Parallel ->
WorkflowVariables -> Env -> Block (matches the table); 'variable'/'loop'/
'parallel' are literal prefixes (REFERENCE.PREFIX); block names normalize via
toLowerCase + strip spaces; an unmatched reference is genuinely left in place
(resolver returns undefined -> the replacer emits the raw match). One claim
tightened: {{KEY}} is a different syntax and can never collide with
angle-bracket references, so the precedence sentence now scopes collisions to
the angle-bracket sources with a concrete example (a block named 'variable').

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

* docs(building-agents): workflow-as-tool is agent-decided, not the Workflow block

The choosing page defined workflow-as-tool as the Workflow block (path-decided),
contradicting its own name and the comparison table's premise. Verified against
the product: workflow_executor is an agent tool — you pick the workflow in the
Agent block's tool list, the model decides when to call it and supplies the
inputMapping (user-or-llm), inputs arrive at the child's Start trigger.

Rewritten agent-first: the section defines it as a workflow handed to an agent
as one callable tool, the lead scorer gains a Deep Enrich workflow tool chip on
the agent (diagram updated), and the deterministic Workflow block becomes the
explicit contrast in a callout — same child workflow, the difference is who
decides, mirroring the block/agent-tool contrast. Table row corrected to
"The agent"; the summary paragraph follows.

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

* docs: theme-aware previews + enrichments vs workflow groups split

Light-mode support for every preview component (WorkflowPreview canvas, nodes,
containers, edges, lightbox, BlockPreview, OutputBundle, BlockInspector): a
wp-scope token block in the docs global stylesheet whose values mirror the OG
repository's globals.css in both modes (surfaces, borders, --workflow-edge,
text tiers, the --badge-* type-badge palette). Every hardcoded hex swapped to a
--wp-* var; brand colors, selection blue, and error red stay literal.

tables/workflow-columns: separated the two group kinds per the contract's
workflowGroupType enum ('manual' | 'enrichment'). New "Two kinds of groups"
section opens with the + New column menu capture (Enrichments above the types,
Workflow below); Enrichments documented from the code-defined registry (company
domain, company info, email verification, phone number, work email) including
the provider-cascade behavior that produces Not found cells; the Company Info
panel capture is now correctly labeled as an enrichment config; workflow groups
keep the Configure workflow panel. Shared machinery generalized under "How
groups run"; the cascade section names which stage is which kind; the two
portrait screenshots render smaller.

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

* docs(tables): don't enumerate the enrichment catalog; don't assert a group's kind

Two corrections on workflow columns: the prose no longer lists the enrichment
catalog (growable, not procedurally tracked — it now describes the category and
points at the Enrichments panel; the provider-cascade/Not-found explanation
stays, it's behavior not catalog), and the page no longer asserts which kind
the example's Company Domain / Company Info groups are (Company Info may be a
user-built workflow, not the built-in). The input/output bindings capture moved
to "How groups run" as the kind-agnostic illustration; only Lead Score — whose
panel shows the workflow picker — is named as a workflow group.

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

* docs(components): per-branch source handles — conditions and routers finally branch

WorkflowPreview's node only ever had one header source handle, so every
condition/router example fanned both edges out of a single point and never
showed the if/else rows the real canvas (and the BlockPreview hero specs)
render. PreviewBlock now supports `branches` (each rendered as a row with its
own right-edge source handle, id `branch-<id>`) and `showError` (red error
handle), mirroring the executor's per-branch condition-true/condition-false and
router-<route> handle model. A block with branches emits from them, not the
header.

Every affected example rewired (13 workflows): the three condition examples,
status-per-branch, credential routing, and the webhook-trigger check route
their edges through branch-if/branch-else with the expression on the If row and
an explicit else; the three router examples list their actual routes as branch
rows (Sales/Support/Billing, Product/Bug report, Enterprise/Self-serve); the
terminal gates (variables retry, evaluator gate, the three guardrails gates)
show dangling if/else branch rows like the canvas does.

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

* docs(components): inspector shows branch rows

Moving condition expressions from rows into branches emptied the lightbox
inspector for condition/router blocks — it only mapped rows to fields. Branches
now map too: each branch renders as a field (If with its expression as code,
else as an empty control, router routes by name).

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

* docs(components): branch handle ids match the app's workflow representation

Verified against the source after the branch-handles work: the canvas emits
condition-${cond.id} handles per condition row (workflow-block.tsx) and Router
V2 uses router-${routeId} port handles, and edges carry those ids as
sourceHandle — the docs' invented branch- prefix was a gratuitous divergence
that the planned fromWorkflowState() adapter would have had to translate. The
node now uses the authored branch id as the handle id directly, and every
example authors ids in the app's own scheme (condition-if/condition-else,
router-<route>), so example edges now match real workflow edges verbatim.

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

* docs: agent skills mint /integrations/ docs links and describe the new output

The add-integration/add-block/validate-integration skills — what Claude Code
follows when integrations land on staging — still taught the old layout:
docsLink templates pointing at docs.sim.ai/tools/{service} and 'generates
tools/{service}.mdx'. Updated so that once this PR merges, the instructions on
staging produce the new way by themselves: /integrations/ docsLinks, the
per-service page description, and the don't-hand-edit/manual-content pointer.

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

* docs(workflows): execution semantics, not simultaneity

The concurrency section drifted into 'run at the same time' framing across two
accuracy passes — but the semantics are non-blocking execution: a block starts
the moment its dependencies finish and waits on nothing else. Section retitled
'Blocks run as soon as they can', the rule stated in two plain sentences, the
duplicated pre-image example narration gone (the post-image caption carries it).

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

* docs(workflows): errors are execution semantics — own section on how-it-runs

Failure behavior was buried inside 'Watching a run' (the live-UI section). Now
a first-class 'When a block fails' section in the execution story: an error
fails the run (in-flight blocks finish, nothing new starts) unless the block's
error port is connected, in which case the run follows the error path.

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

* docs: data-driven additions from the platform-metrics read

Three targeted edits from the sim-internals analysis, each carrying an inline
{/* why */} provenance comment so future editorial passes know the data behind
it:

- workflows/how-it-runs gains "How long a run can take" — run timeouts are the
  only hard-error class provable at scale (2,415 five-minute timeouts in 14
  days); limits verified in lib/core/execution-limits/types.ts (5 min free /
  50 min paid sync, 90 min async, env-overridable).
- getting-started gains an "if the run doesn't go green" callout at the Test
  step — the largest funnel drop is created-workflow -> first-successful-run
  (92% -> 49%), and this is the stall point.
- function/api Best Practices: the existing error-path bullets get a guard
  comment (<1% of deployed workflows connect an error port — under-adopted,
  not under-needed) instead of duplicate bullets.
- visuals manifest: capture priority reordered by integration adoption
  (Sheets, Gmail, Telegram, WhatsApp, ...).

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

* docs: regenerate after staging merge (integration validation batch + Gong tools)

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

* docs: rename Building agents -> Agents; URLs match the settled IA

The section's pages now live where the sidebar says they do:
building-agents/ -> agents/, and the stray top-level /mcp and /skills fold in
as /agents/mcp and /agents/skills (they were always part of the agents story —
the URLs predated the IA settling). Sidebar section header is now "Agents",
link labels updated, and every old URL 308s: /building-agents(/*) -> /agents(/*),
/mcp, /skills, plus the existing capabilities/ and tools/custom-tools redirect
destinations retargeted. Verified: all five new pages render and every old-URL
class redirects correctly.

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

* docs(workflows): connections gets its video, an FAQ, and accurate output examples

The reorg dropped two things from the old tags page that belonged on the
connections reference: the connections.mp4 walkthrough (restored after the
intro) and the FAQ (rebuilt in the robust JSX form — resolver order, name
normalization, env-var syntax pointer, didn't-run behavior, array indexing,
Function-block formatting; answers aligned with the since-verified resolver
facts, including unmatched-references-left-in-place).

Editorial/accuracy pass on the output-shape tabs while in there: stale gpt-4o
and gpt-5 examples now claude-sonnet-4-6, the Agent tokens shape corrected to
the verified { input, output, total } (the page contradicted blocks/agent), and
the dubious cost: [] line dropped — the example now matches the real run
inspector.

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

* docs: regenerate after staging merge — sim trigger, enrichment + logs blocks, re-shown DB integrations

Staging's #4941 added the Sim workspace-event trigger (hand-written page adopted
into Core Triggers), the Enrichment and Logs blocks (category 'blocks' — added
to NATIVE_RESOURCE_BLOCK_TYPES so they live in the integrations catalog like
table/knowledge/memory), and re-categorized mysql/postgresql/sftp/smtp/ssh back
to visible tools (their pages return to the catalog). Generator sets merged as
the union of both sides (sim in HANDWRITTEN_TRIGGER_DOCS + SKIP_TRIGGER_PROVIDERS,
enrichment in the icon allowlist).

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

* docs: regenerate after staging merge (CodePipeline); suppress sim trigger from catalog

The native Sim workspace-event trigger is documented at triggers/sim — the
block writer no longer emits an integrations page for it (skip + canonical-set
exclusion). CodePipeline (#4945) lands in the catalog in the Actions format.

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

* docs(blocks): cross-link the Memory block from the Agent memory section

Final loss audit found the old page's pointer from built-in agent memory to the
standalone Memory block had been dropped; one line restores it.

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

* docs: URLs now mirror the sidebar — sections own their pages

Every page lives at a path matching its meta.json section, done now while none
of these URLs are publicly live (the last free window before merge):

- Workflows owns its accordions: /blocks/* -> /workflows/blocks/*,
  /triggers/{start,schedule,webhook,rss,table,sim} -> /workflows/triggers/*,
  /deployment/* -> /workflows/deployment/*
- Mothership owns Mailer: /mailer -> /mothership/mailer
- Workspaces & Access folds into Platform, sequenced concept-first with the
  reference tail last: /platform/{workspaces,organization,permissions,
  credentials,costs}, then platform/self-hosting/*, platform/enterprise/*
  (from /workspaces/fundamentals+organization, /permissions/roles-and-
  permissions, /credentials, /costs, /self-hosting/*, /enterprise/*)

All internal links swept (0 broken in a full-tree resolver sweep), root
meta.json repointed, and every previously-live URL 308s to its new home —
including retargeted destinations of existing redirects so chains stay
single-hop (verified: /execution/chat reaches /workflows/deployment/chat in
one hop), and the native-trigger rule ordered after the enumerated
integration-trigger redirects so /triggers/gmail still reaches
/integrations/gmail. Production build passes.

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

* docs: untrack .plans/ (local agent planning files)

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

* docs(preview): tool chips use the EMCN ChipTag chrome

The canvas previews' tool chips were ad-hoc (5px radius, header surface, plain
border). The app's canonical chip chrome is the ChipTag family: 20px tall,
rounded-md, px-1, gap-1.5, --surface-5 light / --surface-4 dark with an inset
--border-1 ring and --text-body label. Mirrored those values into --wp-chip-*
tokens (both modes) and restyled the chip; the integration's brand-color icon
square stays, sized to the chip.

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

* chore(blocks): one-time shift of all docsLinks to the new docs URLs

Block definitions are the patterns coding agents copy from, so redirects alone
leave new blocks minting dead conventions. Every docs.sim.ai link in apps/sim
now points at the final URL scheme: /tools/<slug> -> /integrations/<slug>
(433 links), /blocks/<core> -> /workflows/blocks/<core> (knowledge/enrichment/
logs -> /integrations/*), native /triggers/* -> /workflows/triggers/*,
/mcp -> /agents/mcp, /self-hosting + /enterprise -> /platform/*, plus the
llms.txt listings and the blocks.test.ts assertions.

Verified every rewritten target against the docs tree: all resolve except ten
hidden blocks (vision, spotify, thinking, tts...) and a2a whose links were
already dead pre-reorg — no regressions introduced.

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

* docs: ignore .plans/ (local agent planning files)

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

* docs(files): align every File-block claim with the shipped file_v5 block

Accuracy audit against apps/sim/blocks/blocks/file.ts (FileV5Block, the visible
block) and tools/file/*:

- The block has FIVE operations, not four — Get Content was missing entirely.
- Read outputs file objects only; the page claimed it also returned extracted
  text. Text comes from Get Content (contents, per file) or Fetch
  (combinedContent) — table, prose, and the Fetch callout corrected.
- Functions CAN read files: sim.files.readText/readBase64 exist in the sandbox
  (isolated-vm-worker.cjs), so "doesn't reach into workspace storage" is gone;
  the section now teaches Get Content text or sim.files on the file object.
- Workspace file IDs are wf_<shortId> (workspace-file-manager.ts:511), not f_.
- Stale "such as Claude or GPT-4o" vision parenthetical dropped.
- "File block reference" card pointed at /files (the section overview); now
  /integrations/file.
- FILE_SUMMARY example agent consumed <file.combinedContent>, which Read never
  produces — now binds the file object to the Files input.
- passing-files.mdx: combinedContent scoped to Fetch, contents documented.

Verified intact: Write's numeric-suffix collision behavior, Fetch's auth
headers, Append-by-name, and the file-object shape.

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

* docs: keyboard-shortcuts audited against the command registry; cut legacy workspace detail

Every binding verified against commands-utils.ts (the global registry),
workflow.tsx, and table-grid.tsx. Three fixes: tables Mod+A (select all rows)
doesn't exist — the real bindings are Shift+Space (select row, was misworded
as a toggle) and the undocumented Mod+Space (select column); the global
Mod+Shift+A row conflated two commands — add-agent (Mod+Shift+A) and
add-workflow (Mod+Shift+P) are separate. All 29 other documented shortcuts
confirmed accurate, including tables clipboard (native copy/cut/paste events)
and Mod+Y redo (tables only — correctly absent from the workflow editor
section).

Also drops the grandfathered_shared workspace paragraph — internal billing
taxonomy, not something a reader can act on.

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

* docs: apply Theodore's accuracy feedback

- getting-started: workflow creation is the + button next to Workflows in the
  sidebar (no "New Workflow" button exists); Exa/Linkup no longer need
  user-supplied API keys on hosted Sim (apiKey is hideWhenHosted in the Exa
  block) — step and FAQ updated.
- workflows overview: chat and API are entry points of the Start trigger, not
  separate triggers — the "swap in a chat/API trigger" sentence now matches
  triggers/start's own model.
- variables: names cannot contain periods — the resolver reads everything
  after the first dot as a path into the value (executor/variables/resolvers/
  workflow.ts splits on dots) — constraint now stated where name normalization
  is taught.

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

* docs: Python sandbox package list (verified) + agent/agents cross-linking

Function block: the Python callout's "common packages like matplotlib" becomes
the actual package list, grouped by use. Sources verified 2026-06-10 and cited
in an inline provenance comment: E2B's code-interpreter template requirements
(the base Sim's mothership-shell template builds from) plus Sim's three pip
additions (awscli/yq/csvkit, per the copilot repo's template.ts via
sim-internals). Versions omitted so the list doesn't rot on routine bumps.

Agent surfaces deduplicated by direction: blocks/agent's Tools section now
links custom tools and MCP and points at the Agents concept page for tool
sourcing; agents/index drops its duplicated Auto/Force/None enumeration in
favor of the block reference, which owns config mechanics.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:39:57 -07:00
Waleed 11fa96cac0 chore(deps): bump next to 16.2.5 for CVE-2026-44578 SSRF fix (#4606)
* chore(deps): bump next to 16.2.5 for CVE-2026-44578 SSRF fix

* chore(deps): bump next to 16.2.6 for full May 2026 security release coverage
2026-05-14 14:34:05 -07:00
Waleed 48331451b0 chore(deps): upgrade next.js to 16.2.4 (#4460)
* chore(deps): upgrade next.js to 16.2.4

- Bump next and @next/env to 16.2.4 across root, apps/sim, apps/docs
- Replace next-runtime-env's env() helper (calls unstable_noStore(), rejected by Next 16.2 outside request scope) with a direct window.__ENV / process.env getter
- Add export const dynamic = 'force-dynamic' on landing /privacy and /terms pages so NEXT_PUBLIC_* runtime env reads aren't baked at build

* fix(whitelabel): force dynamic rendering for manifest.ts

Without this, NEXT_PUBLIC_BRAND_* values are baked into the manifest at build time. Pairs with the next-runtime-env removal in the prior commit, restoring Docker runtime injection for whitelabel deployments.

* fix(oauth): wrap consent page useSearchParams in Suspense

Next 16.2's stricter prerender check fails the build when useSearchParams() is used without a Suspense boundary. Splits the client component into an outer wrapper and inner body.

* fix(whitelabel): force dynamic rendering for landing segment

Client components in (landing) (e.g. Navbar) read NEXT_PUBLIC_BRAND_* via getEnv. Without this, SSR prerender would bake the build-time process.env values into HTML, mismatching window.__ENV after hydration in Docker runtime-env deployments. Cascades to all landing routes via the layout.

* revert(whitelabel): drop force-dynamic from landing layout

Cascading force-dynamic neutered dynamicParams = false + generateStaticParams on /blog/[slug], /integrations/[slug], /models/[provider], /models/[provider]/[model] — killing static prerender for SEO-critical pages. The hydration concern only materializes for whitelabel Docker deployments where build-time and runtime NEXT_PUBLIC_BRAND_* differ; those deployments can set the vars at build instead. Keeping force-dynamic on /privacy, /terms, and /manifest where it actually matters.

* fix(prerender): wrap useSearchParams callsites for Next 16.2

Next 16.2 fails the build when a client component using useSearchParams() is statically prerendered without a Suspense boundary.

- Wrap landing Navbar in Suspense (imported by /oauth/consent and other pages)
- Add force-dynamic to reset-password, invite/[id], and unsubscribe pages whose client bodies call useSearchParams

* fix(navbar): preserve SSR HTML, drop Suspense bailout

Reading useSearchParams() forced a Suspense fallback that emitted no navbar HTML during SSR — leaving crawlers and no-JS users without nav. The 'home' query param only affects client-side link targets, so read it from window.location in an effect after hydration. Restores full SSR navbar markup.

* chore: trim verbose comments in next.js upgrade

The force-dynamic export name is self-documenting; the remaining env.ts comment is tightened to the essential WHY (why we don't use next-runtime-env's helper).
2026-05-06 11:08:01 -07:00
Waleed d517415c77 chore(docs): upgrade fumadocs to latest minor versions (#4462)
* chore(docs): upgrade fumadocs to latest minor versions

- fumadocs-core: 16.6.7 -> 16.8.5
- fumadocs-ui: 16.6.7 -> 16.8.5
- fumadocs-mdx: 14.2.8 -> 14.3.2
- fumadocs-openapi: 10.3.13 -> 10.8.1
- migrate deprecated sidebar.tabs to top-level tabs prop
- fix pre-existing typo (slots.paremeters) surfaced by stricter openapi types

* fix(docs): revert sidebar.tabs migration to keep deploy compat

The top-level tabs prop is only on fumadocs-ui 16.7+; deploy env was
still resolving an older type and failing typecheck. sidebar.tabs is
deprecated but still functional — keep it for now.
2026-05-05 17:54:18 -07:00
WaleedandClaude Opus 4.7 45bf396968 fix(deps): bump drizzle-orm 0.45.2 + adopt MCP SDK 1.25.3 native types (#4252)
* fix(deps): bump drizzle-orm to 0.45.2 (GHSA-gpj5-g38j-94v9)

Resolves Dependabot alert #98. Drizzle ORM <0.45.2 improperly escaped
quoted SQL identifiers, allowing SQL injection via untrusted input
passed to APIs like sql.identifier() or .as().

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(mcp): adopt native SDK types after @modelcontextprotocol/sdk 1.25.3 bump

Replace hand-written schema/annotation shapes with the SDK's exported
Tool, JSONRPCResultResponse, and Tool['annotations'] types so changes
upstream flow through automatically.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(types): use drizzle $inferSelect for row types

Replace hand-written interfaces that duplicated schema shape with
typeof table.$inferSelect aliases for webhook, workflow, and
workspaceFiles rows. Also simplify metadata insert/update to use
.returning() instead of field-by-field copies.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(uploads): fall through to INSERT if restore-deleted row races a hard delete

If a hard delete races between the initial SELECT and the restore UPDATE,
.returning() yields no row. Previously the function would return undefined
and silently violate the Promise<FileMetadataRecord> contract. Now the
function falls through to the INSERT path, which already handles
uniqueness races via the 23505 catch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(uploads): align metadata.ts with global standards

Replace dynamic uuid import with generateId() per @sim/utils/id
convention, narrow the error catch off `any`, and convert the inline
comment to TSDoc.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 19:52:15 -07:00
Waleed 8837f14194 feat(home): expand template examples with 83 categorized templates (#3592)
* feat(home): expand template examples with 83 categorized templates

- Extract template data into consts.ts with rich categorization (category, modules, tags)
- Expand from 6 to 83 templates across 7 categories: sales, support, engineering, marketing, productivity, operations
- Add show more/collapse UI with category groupings for non-featured templates
- Add connector-powered knowledge search templates (Gmail, Slack, Notion, Jira, Linear, Salesforce, etc.)
- Add platform-native templates (document summarizer, bulk data classifier, knowledge assistant, etc.)
- Optimize prompts for mothership execution with explicit resource creation and integration names
- Add tags field for future cross-cutting filtering by persona, pattern, and domain
- React 19.2.1 → 19.2.4 upgrade

* fix(home): remove WhatsApp customer notifications template

* fix(home): add aria-expanded to toggle button, skip popular in expanded view

* fix(home): fix category display order, add aria-label to template cards
2026-03-14 17:53:12 -07:00
5b9f0d73c2 feat(mothership): mothership (#3411)
* Fix lint

* improvement(sidebar): loading

* fix(sidebar): use client-generated UUIDs for stable optimistic updates (#3439)

* fix(sidebar): use client-generated UUIDs for stable optimistic updates

* fix(folders): use zod schema validation for folder create API

Replace inline UUID regex with zod schema validation for consistency
with other API routes. Update test expectations accordingly.

* fix(sidebar): add client UUID to single workflow duplicate hook

The useDuplicateWorkflow hook was missing newId: crypto.randomUUID(),
causing the same temp-ID-swap issue for single workflow duplication
from the context menu.

* fix(folders): avoid unnecessary Set re-creation in replaceOptimisticEntry

Only create new expandedFolders/selectedFolders Sets when tempId
differs from data.id. In the common happy path (client-generated UUIDs),
this avoids unnecessary Zustand state reference changes and re-renders.

* Mothership block logs

* Fix mothership block logs

* improvement(knowledge): make connector-synced document chunks readonly (#3440)

* improvement(knowledge): make connector-synced document chunks readonly

* fix(knowledge): enforce connector chunk readonly on server side

* fix(knowledge): disable toggle and delete actions for connector-synced chunks

* Job exeuction logs

* Job logs

* fix(connectors): remove unverifiable requiredScopes for Linear connector

* fix(connectors): remove legacy requiredScopes from Jira and Confluence connectors

Jira and Confluence OAuth tokens don't return legacy scope names like
read:jira-work or read:confluence-content.all, causing the 'Update access'
banner to always appear. Set requiredScopes to empty array like Linear.

* feat(tasks): add rename to task context menu (#3442)

* Revert "fix(connectors): remove legacy requiredScopes from Jira and Confluence connectors"

This reverts commit a0be3ff414.

* fix(connectors): restore Linear connector requiredScopes

Linear OAuth does return scopes in the token response. The previous
fix of emptying requiredScopes was based on an incorrect assumption.
Restoring requiredScopes: ['read'] as it should work correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(knowledge): pass workspaceId to useOAuthCredentials in connector card

The ConnectorCard was calling useOAuthCredentials(providerId) without
a workspaceId, causing the credentials API to return an empty array.
This meant the credential lookup always failed, getMissingRequiredScopes
received undefined, and the "Update access" banner always appeared.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix oauth link callback from mothership task

* feat(connectors): add Fireflies connector and API key auth support (#3448)

* feat(connectors): add Fireflies connector and API key auth support

Extend the connector system to support both OAuth and API key authentication
via a discriminated union (`ConnectorAuthConfig`). Add Fireflies as the first
API key connector, syncing meeting transcripts via the Fireflies GraphQL API.

Schema changes:
- Make `credentialId` nullable (null for API key connectors)
- Add `encryptedApiKey` column (AES-256-GCM encrypted, null for OAuth)

This eliminates the `'_apikey_'` sentinel and inline `sourceConfig._encryptedApiKey`
patterns, giving each auth mode its own clean column.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(fireflies): allow 0 for maxTranscripts (means unlimited)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Add context

* fix(fireflies): correct types from live API validation (#3450)

* fix(fireflies): correct types from live API validation

- speakers.id is number, not string (API returns 0, 1, 2...)
- summary.action_items is a single string, not string[]
- Update formatTranscriptContent to handle action_items as string

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(fireflies): correct tool types from live API validation

- FirefliesSpeaker.id: string -> number
- FirefliesSentence.speaker_id: string -> number
- FirefliesSpeakerAnalytics.speaker_id: string -> number
- FirefliesSummary.action_items: string[] -> string
- FirefliesSummary.outline: string[] -> string
- FirefliesSummary.shorthand_bullet: string[] -> string
- FirefliesSummary.bullet_gist: string[] -> string
- FirefliesSummary.topics_discussed: string[] -> string

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat(knowledge): add connector tools and expand document metadata (#3452)

* feat(knowledge): add connector tools and expand document metadata

* fix(knowledge): address PR review feedback on new tools

* fix(knowledge): remove unused params from get_document transform

* refactor, improvement

* fix: correct knowledge block canonical pair pattern and subblock migration

- Rename manualDocumentId to documentId (advanced subblock ID should match
  canonicalParamId, consistent with airtable/gmail patterns)
- Fix documentSelector.dependsOn to reference knowledgeBaseSelector (basic
  depends on basic, not advanced)
- Remove unnecessary documentId migration (ID unchanged from main)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* lint

* fix: resolve post-merge test and lint failures

- airtable: sync tableSelector condition with tableId (add getSchema)
- backfillCanonicalModes test: add documentId mode to prevent false backfill
- schedule PUT test: use invalid action string now that disable is valid
- schedule execute tests: add ne mock, sourceType field, use
  mockReturnValueOnce for two db.update calls
- knowledge tools: fix biome formatting (single-line arrow functions)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fixes

* Fixes

* Clean vfs

* Fix

* Fix lint

* fix(connectors): add rate limiting, concurrency controls, and bug fixes (#3457)

* fix(connectors): add rate limiting, concurrency controls, and bug fixes across knowledge connectors

- Add Retry-After header support to fetchWithRetry for all 18 connectors
- Batch concurrent API calls (concurrency 5) in Dropbox, Google Docs, Google Drive, OneDrive, SharePoint
- Batch concurrent API calls (concurrency 3) in Notion to match 3 req/s limit
- Cache GitHub tree in syncContext to avoid re-fetching on every pagination page
- Batch GitHub blob fetches with concurrency 5
- Fix GitHub base64 decoding: atob() → Buffer.from() for UTF-8 safety
- Fix HubSpot OAuth scope: 'tickets' → 'crm.objects.tickets.read' (v3 API)
- Fix HubSpot syncContext key: totalFetched → totalDocsFetched for consistency
- Add jitter to nextSyncAt (10% of interval, capped at 5min) to prevent thundering herd
- Fix Date consistency in connector DELETE route

* fix(connectors): address PR review feedback on retry and SharePoint batching

- Remove 120s cap on Retry-After — pass all values through to retry loop
- Add maxDelayMs guard: if Retry-After exceeds maxDelayMs, throw immediately
  instead of hammering with shorter intervals (addresses validate timeout concern)
- Add early exit in SharePoint batch loop when maxFiles limit is reached
  to avoid unnecessary API calls

* fix(connectors): cap Retry-After at maxDelayMs instead of aborting

Match Google Cloud SDK behavior: when Retry-After exceeds maxDelayMs,
cap the wait to maxDelayMs and log a warning, rather than throwing
immediately. This ensures retries are bounded in duration while still
respecting server guidance within the configured limit.

* fix(connectors): add early-exit guard to Dropbox, Google Docs, OneDrive batch loops

Match the SharePoint fix — skip remaining batches once maxFiles limit
is reached to avoid unnecessary API calls.

* improvement(turbo): align turborepo config with best practices (#3458)

* improvement(turbo): align turborepo config with best practices

* fix(turbo): address PR review feedback

* fix(turbo): add lint:check task for read-only lint+format CI checks

lint:check previously delegated to format:check which only checked
formatting. Now it runs biome check (no --write) which enforces both
lint rules and formatting without mutating files.

* upgrade turbo

* improvement(perf): apply react and js performance optimizations across codebase (#3459)

* improvement(perf): apply react and js performance optimizations across codebase

- Parallelize independent DB queries with Promise.all in API routes
- Defer PostHog and OneDollarStats via dynamic import() to reduce bundle size
- Use functional setState in countdown timers to prevent stale closures
- Replace O(n*m) .filter().find() with Set-based O(n) lookups in undo-redo
- Use .toSorted() instead of .sort() for immutable state operations
- Use lazy initializers for useState(new Set()) across 20 components
- Remove useMemo wrapping trivially cheap expressions (typeof, ternary, template strings)
- Add passive: true to scroll event listener

* fix(perf): address PR review feedback

- Extract IIFE Set patterns to named consts for readability in use-undo-redo
- Hoist Set construction above loops in BATCH_UPDATE_PARENT cases
- Add .catch() error handler to PostHog dynamic import
- Convert session-provider posthog import to dynamic import() to complete bundle split

* fix(analytics): add .catch() to onedollarstats dynamic import

* improvement(resource): tables, files

* improvement(resources): all outer page structure complete

* refactor(queries): comprehensive TanStack Query best practices audit (#3460)

* refactor: comprehensive TanStack Query best practices audit and migration

- Add AbortSignal forwarding to all 41 queryFn implementations for proper request cancellation
- Migrate manual fetch patterns to useMutation hooks (useResetPassword, useRedeemReferralCode, usePurchaseCredits, useImportWorkflow, useOpenBillingPortal, useAllowedMcpDomains)
- Migrate standalone hooks to TanStack Query (use-next-available-slot, use-mcp-server-test, use-webhook-management, use-referral-attribution)
- Fix query key factories: add missing `all` keys, replace inline keys with factory methods
- Fix optimistic mutations: use onSettled instead of onSuccess for cache reconciliation
- Replace overly broad cache invalidations with targeted key invalidation
- Remove keepPreviousData from static-key queries where it provides no benefit
- Add staleTime to queries missing explicit cache duration
- Fix `any` type in UpdateSettingParams with proper GeneralSettings typing
- Remove dead code: loadingWebhooks/checkedWebhooks from subblock store, unused helper functions
- Update settings components (general, debug, referral-code, credit-balance, subscription, mcp) to use mutation state instead of manual useState for loading/error/success

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove unstable mutation object from useCallback deps

openBillingPortal mutation object is not referentially stable,
but .mutate() is stable in TanStack Query v5. Remove from deps
to prevent unnecessary handleBadgeClick recreations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add missing byWorkflows invalidation to useUpdateTemplate

The onSettled handler was missing the byWorkflows() invalidation
that was dropped during the onSuccess→onSettled migration. Without
this, the deploy modal (useTemplateByWorkflow) would show stale data
after a template update.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add TanStack Query best practices to CLAUDE.md and cursor rules

Add comprehensive React Query best practices covering:
- Hierarchical query key factories with intermediate plural keys
- AbortSignal forwarding in all queryFn implementations
- Targeted cache invalidation over broad .all invalidation
- onSettled for optimistic mutation cache reconciliation
- keepPreviousData only on variable-key queries
- No manual fetch in components rule
- Stable mutation references in useCallback deps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review feedback

- Fix syncedRef regression in use-webhook-management: only set
  syncedRef.current=true when webhook is found, so re-sync works
  after webhook creation (e.g., post-deploy)
- Remove redundant detail(id) invalidation from useUpdateTemplate
  onSettled since onSuccess already populates cache via setQueryData

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address second round of PR review feedback

- Reset syncedRef when blockId changes in use-webhook-management so
  component reuse with a different block syncs the new webhook
- Add response.ok check in postAttribution so non-2xx responses
  throw and trigger TanStack Query retry logic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use lists() prefix invalidation in useCreateWorkspaceCredential

Use workspaceCredentialKeys.lists() instead of .list(workspaceId) so
filtered list queries are also invalidated on credential creation,
matching the pattern used by update and delete mutations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address third round of PR review feedback

- Add nullish coalescing fallback for bonusAmount in referral-code
  to prevent rendering "undefined" when server omits the field
- Reset syncedRef when queryEnabled becomes false so webhook data
  re-syncs when the query is re-enabled without component remount

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address fourth round of PR review feedback

- Add AbortSignal to testMcpServerConnection for consistency
- Wrap handleTestConnection in try/catch for mutateAsync error handling
- Replace broad subscriptionKeys.all with targeted users()/usage() invalidation
- Add intermediate users() key to subscription key factory for prefix matching
- Add comment documenting syncedRef null-webhook behavior
- Fix api-keys.ts silent error swallowing on non-ok responses
- Move deployments.ts cache invalidation from onSuccess to onSettled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: achieve full TanStack Query best practices compliance

- Add intermediate plural keys to api-keys, deployments, and schedules
  key factories for prefix-based invalidation support
- Change copilot-keys from refetchQueries to invalidateQueries
- Add signal parameter to organization.ts fetch functions (better-auth
  client does not support AbortSignal, documented accordingly)
- Move useCreateMcpServer invalidation from onSuccess to onSettled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* ran lint

* Fix tables row count

* Update mothership to match copilot in logs

* improvement(resource): layout

* fix(knowledge): compute KB tokenCount from documents instead of stale column (#3463)

The knowledge_base.token_count column was initialized to 0 and never
updated. Replace with COALESCE(SUM(document.token_count), 0) in all
read queries, which already JOIN on documents with GROUP BY.

* improvement(resources): layout and items

* feat(knowledge): add v1 knowledge base API, Obsidian/Evernote connectors, and docs (#3465)

* feat(knowledge): add v1 knowledge base API, Obsidian/Evernote connectors, and docs

- Add v1 REST API for knowledge bases (CRUD, document management, vector search)
- Add Obsidian and Evernote knowledge base connectors
- Add file type validation to v1 file and document upload endpoints
- Update OpenAPI spec with knowledge base endpoints and schemas
- Add connectors documentation page
- Apply query hook formatting improvements

* fix(knowledge): address PR review feedback

- Remove validateFileType from v1/files route (general file upload, not document-only)
- Reject tag filters when searching multiple KBs (tag defs are KB-specific)
- Cache tag definitions to avoid duplicate getDocumentTagDefinitions call
- Fix Obsidian connector silent empty results when syncContext is undefined

* improvement(connectors): add syncContext to getDocument, clean up caching

- Update docs to say 20+ connectors
- Add syncContext param to ConnectorConfig.getDocument interface
- Use syncContext in Evernote getDocument to cache tag/notebook maps
- Replace index-based cache check with Map keyed by KB ID in search route

* fix(knowledge): address second round of PR review feedback

- Fix Zod .default('text') overriding tag definition's actual fieldType
- Fix encodeURIComponent breaking multi-level folder paths in Obsidian
- Use 413 instead of 400 for file-too-large in document upload
- Add knowledge-bases to API reference docs navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(knowledge): prevent cross-workspace KB access in search

Filter accessible KBs by matching workspaceId from the request,
preventing users from querying KBs in other workspaces they have
access to but didn't specify.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(knowledge): audit resourceId, SSRF protection, recursion depth limit

- Fix recordAudit using knowledgeBaseId instead of newDocument.id
- Add SSRF validation to Obsidian connector (reject private/loopback URLs)
- Add max recursion depth (20) to listVaultFiles

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(obsidian): remove SSRF check that blocks localhost usage

The Obsidian connector is designed to connect to the Local REST API
plugin running on localhost (127.0.0.1:27124). The SSRF check was
incorrectly blocking this primary use case.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(resources): segmented API

* fix(execution): ensure background tasks await post-execution DB status updates (#3466)

The fire-and-forget IIFE in execution-core.ts for post-execution logging could be abandoned when trigger.dev tasks exit, leaving executions permanently stuck in "running" status. Store the promise on LoggingSession so background tasks can optionally await it before returning.

* improvement(resource): sorting and icons

* fix(resource): sorting

* improvement(settings): fix mcp modal, add option to edit JSON and add Sim as an MCP client (#3467)

* improvement(settings): fix mcp modal, add option to edit JSON and add Sim as an MCP client

* added docs link in sidebar

* ack comments

* ack comments

* fixed error msg

* feat(mothership): billing (#3464)

* Billing update

* more billing improvements

* credits UI

* credit purchase safety

* progress

* ui improvements

* fix cancel sub

* fix types

* fix daily refresh for teams

* make max features differentiated

* address bugbot comments

* address greptile comments

* revert isHosted

* address more comments

* fix org refresh bar

* fix ui rounding

* fix minor rounding

* fix upgrade issue for legacy plans

* fix formatPlanName

* fix email dispay names

* fix legacy team reference bugs

* referral bonus in credits

* fix org upgrade bug

* improve logs

* respect toggle for paid users

* fix landing page pro features and usage limit checks

* fixed query and usage

* add unit test

* address more comments

* enterprise guard

* fix limits bug

* pass period start/end for overage

* fix(sidebar): restore drag-and-drop for workflows and folders (#3470)

* fix(sidebar): restore drag-and-drop for workflows and folders

Made-with: Cursor

* update docs, unrelated

* improvement(tables): consolidation

* feat(schedules): add schedule creator modal for standalone jobs

Add modal to create standalone scheduled jobs from the Schedules page.
Includes POST API endpoint, useCreateSchedule mutation hook, and full
modal with schedule type selection, timezone, lifecycle, and live preview.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(schedules): add edit support with context menu for standalone jobs

* style(schedules): apply linter formatting

* improvement: tables, favicon

* feat(files): inline file viewer with text editing (#3475)

* feat(files): add inline file viewer with text editing and create file modal

Add file preview/edit functionality to the workspace files page. Text files
(md, json, txt, yaml, etc.) open in an editable textarea with Cmd/Ctrl+S save.
PDFs render in an iframe. New file button creates empty .md files via a modal.
Uses ResourceHeader breadcrumbs and ResourceOptionsBar for save/download/delete.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(files): add UX polish, PR review fixes, and context menu

- Add unsaved changes guard modal (matching credentials manager pattern)
- Add delete confirmation modal for both viewer and context menu
- Add save status feedback (Save → Saving... → Saved)
- Add right-click context menu with Open, Download, Delete actions
- Add 50MB file size limit on content update API
- Add storage quota check before content updates
- Add response.ok guard on download to prevent corrupt files
- Add skeleton loading for pending file selection (prevents flicker)
- Fix updateContent in handleSave dependency array

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): propagate save errors and remove redundant sizeDiff

- Remove try/catch in TextEditor.handleSave so errors propagate to
  parent, which correctly shows save failure status
- Remove redundant inner sizeDiff declaration that shadowed outer scope

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): remove unused textareaRef

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): move Cmd+S to parent, add save error feedback, hide save for non-text files

- Move Cmd+S keyboard handler from TextEditor to Files so it goes
  through the parent handleSave with proper status management
- Add 'error' save status with red "Save failed" label that auto-resets
- Only show Save button for text-editable file types (md, txt, json, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(files): add save tooltip, deduplicate text-editable extensions

- Add Tooltip on Save button showing Cmd+S / Ctrl+S shortcut
- Export TEXT_EDITABLE_EXTENSIONS from file-viewer and reuse in files.tsx
  instead of duplicating the list inline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: extract isMacPlatform to shared utility

Move isMacPlatform() from global-commands-provider.tsx to
lib/core/utils/platform.ts so it can be reused by files.tsx tooltip
without duplication.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(files): deduplicate delete modal, use shared formatFileSize

- Extract DeleteConfirmModal component to eliminate duplicate modal
  markup between viewer and list modes
- Replace local formatFileSize with shared utility from file-utils.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): fix a11y label lint error and remove mutation object from useCallback deps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): add isDirty guard on handleSave, return proper HTTP status codes

Prevents "Saving → Saved" flash when pressing Cmd+S with no changes.
Returns 404 for file-not-found and 402 for quota-exceeded instead of 500.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): reset isDirty/saveStatus on delete and discard, remove deprecated navigator.platform

- Clear isDirty and saveStatus when deleting the currently-viewed file to
  prevent spurious beforeunload prompts
- Reset saveStatus on discard to prevent stale "Save failed" when opening
  another file
- Remove deprecated navigator.platform, userAgent fallback covers all cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): prevent concurrent saves on rapid Cmd+S, add YAML MIME types

- Add saveStatus === 'saving' guard to handleSave to prevent duplicate
  concurrent PUT requests from rapid keyboard shortcuts
- Add yaml/yml MIME type mappings to getMimeTypeFromExtension

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(files): reuse shared extension constants, parallelize cancelQueries

- Replace hand-rolled SUPPORTED_EXTENSIONS with composition from existing
  SUPPORTED_DOCUMENT/AUDIO/VIDEO_EXTENSIONS in validation.ts
- Parallelize sequential cancelQueries calls in delete mutation onMutate

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): guard handleCreate against duplicate calls while pending

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): show upload progress on the Upload button, not New file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(files): use ref-based guard for create pending state to avoid stale closure

The uploadFile.isPending check was stale because the mutation object
is excluded from useCallback deps (per codebase convention). Using a
ref ensures the guard works correctly across rapid Enter key presses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* cleanup(files): use shared icon import, remove no-op props, wrap handler in useCallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* improvement: tables, dropdown

* improvement(docs): align sidebar method badges and polish API reference styling (#3484)

* improvement(docs): align sidebar method badges and polish API reference styling

* fix(docs): revert className prop on DocsPage for CI compatibility

* fix(docs): restore oneOf schema for delete rows and use rem units in CSS

* fix(docs): replace :has() selectors with direct className for reliable prod layout

The API docs layout was intermittently narrow in production because CSS
:has(.api-page-header) selectors are unreliable in Tailwind v4 production
builds. Apply className="openapi-page" directly to DocsPage and replace
all 64 :has() selectors with .openapi-page class targeting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(docs): bypass TypeScript check for className prop on DocsPage

Use spread with type assertion to pass className to DocsPage, working
around a CI type resolution issue where the prop exists at runtime but
is not recognized by TypeScript in the Vercel build environment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(docs): use inline style tag for grid layout, revert CSS to :has() selectors

The className prop on DocsPage doesn't exist in the fumadocs-ui version
resolved on Vercel, so .openapi-page was never applied and all 64 CSS
rules broke. Revert to :has(.api-page-header) selectors for styling and
use an inline <style> tag for the critical grid-column layout override,
which is SSR'd and doesn't depend on any CSS selector matching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(docs): add pill styling to footer navigation method badges

The footer nav badges (POST, GET, etc.) had color from data-method rules
but lacked the structural pill styling (padding, border-radius, font-size).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(docs): use named grid lines instead of numeric column indices (#3487)

Root cause: the fumadocs grid template has 3 columns in production but
5 columns in local dev. Our CSS used `grid-column: 3 / span 2` which
targeted the wrong column in the 3-column grid, placing content in
the near-zero-width TOC column instead of the main content column.

Fix: use `grid-column: main-start / toc-end` which uses CSS named grid
lines from grid-template-areas, working regardless of column count.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(resource): layout

* improvement: icon, resource header options

* improvement: icons

* fix(files): icon

* feat(tables): column operations, row ordering, V1 API (#3488)

* feat(tables): add column operations, row ordering, V1 columns API, and OpenAPI spec

Adds column rename/delete/type change/constraint updates to the tables module,
row ordering via position column, UI metadata schema, V1 public API for column
operations with rate limiting and audit logging, and OpenAPI documentation.

Key changes:
- Service-layer column operations with validation (name pattern, type compatibility, unique/required constraints)
- Position column on user_table_rows with composite index for efficient ordering
- V1 /api/v1/tables/{tableId}/columns endpoint (POST/PATCH/DELETE) with rate limiting and audit
- Shared Zod schemas extracted to table/utils.ts using COLUMN_TYPES constant
- Targeted React Query invalidation (row vs schema mutations) with consistent onSettled usage
- OpenAPI 3.1.0 spec for columns endpoint with code samples
- Position field added to all row response mappings for consistency
- Sort fallback to position ordering when buildSortClause returns null

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): use specific error prefixes instead of broad "Cannot" match

Prevents internal TypeErrors (e.g. "Cannot read properties of undefined")
from leaking as 400 responses. Now matches only domain-specific errors:
"Cannot delete the last column" and "Cannot set column".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): reject Infinity and NaN in number type compatibility check

Number.isFinite rejects Infinity, -Infinity, and NaN, preventing
non-finite values from passing column type validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): invalidate table list on row create/delete for stale rowCount

Row create and delete mutations now invalidate the table list cache since
it includes a computed rowCount. Row updates (which don't change count)
continue to only invalidate row queries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): add column name length check, deduplicate name gen, reset pagination on clear

- Add MAX_COLUMN_NAME_LENGTH validation to addTableColumn (was missing,
  renameColumn already had it)
- Extract generateColumnName helper to eliminate triplicated logic across
  handleAddColumn, handleInsertColumnLeft, handleInsertColumnRight
- Reset pagination to page 0 when clearing sort/filter to prevent showing
  empty pages after narrowing filters are removed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: hoist tableId above try block in V1 columns route, add detail invalidation to invalidateRowCount

- V1 columns route: `tableId` was declared inside `try` but referenced in
  `catch` logger.error, causing undefined in error logs. Hoisted `await params`
  above try in all three handlers (POST, PATCH, DELETE).
- invalidateRowCount: added `tableKeys.detail(tableId)` invalidation since the
  single-table GET response includes `rowCount`, which becomes stale after
  row create/delete without this.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add position to all row mutation responses, remove dead filter code

- Add `position` field to POST (single + batch) and PATCH row responses
  across both internal and V1 routes, matching GET responses and OpenAPI spec.
- Remove unused `filterConfig`, `handleFilterToggle`, `handleFilterClear`,
  and `activeFilters` — dead code left over from merge conflict resolution.
  `handleFilterApply` (the one actually wired to JSX) is preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: invalidateTableSchema now also invalidates table list cache

Column add/rename/delete/update mutations now invalidate tableKeys.list()
since the list endpoint returns schema.columns for each table. Without this,
the sidebar table list would show stale column schemas until staleTime expires.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: replace window.prompt/confirm with emcn Modal dialogs

Replace non-standard browser dialogs with proper emcn Modal components
to match the existing codebase pattern (e.g. delete table confirmation).

- Column rename: Modal with Input field + Enter key support
- Column delete: Modal with destructive confirmation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* update schedule creation ui and run lint

* improvement: logs

* improvement(tables): multi-select and efficiencies

* Table tools

* improvement(folder-selection): folder deselection + selection order should match visual

* fix(selections): more nested folder inaccuracies

* Tool updates

* Store tool call results

* fix(landing): wire agent input to mothership

* feat(mothership): resource viewer

* fix tests

* fix(streaming): smoother streaming with throttled rendering, ResizeObserver scroll, and batched updates (#3471)

* fix(streaming): smoother streaming with throttled rendering, ResizeObserver scroll, and batched updates

- Add useThrottledValue hook (100ms trailing-edge throttle) to gate DOM re-renders during streaming across all chat surfaces
- Replace 100ms setInterval scroll polling with ResizeObserver-based auto-scroll, programmatic scroll timestamp tracking, and nested [data-scrollable] region handling
- Extract processContentBuffer from inline content handler for cleaner code organization in copilot SSE handlers
- Add RAF-based update batching (50ms max interval) to floating chat and home chat streaming paths
- Add useProgressiveList hook for progressive rendering of long conversation histories via requestAnimationFrame

Made-with: Cursor

* ack PR comments

* fix search modal

* more comments

* ack comments

* count

* ack comments

* ack comment

* improvement(mothership): worklfow resource

* Fix tool call persistence in chat

* Tool results

* Fix error status

* File uploads to mothership

* feat(templates): landing page templates workflow states

* improvement(mothership): chat stability

* improvement(mothership): chat history and stability

* improvement(tables): click-to-select navigation, inline rename, column resize (#3496)

* improvement(tables): click-to-select navigation, inline rename, column resize

* fix(tables): address PR review comments

- Add doneRef guard to useInlineRename preventing Enter+blur double-fire
- Fix PATCH error handler: return 500 for non-validation errors, fix unreachable logger.error
- Stop click propagation on breadcrumb rename input

* fix(tables): add rows-affected check in renameTable service

Prevents silent no-op when tableId doesn't match any record.

* fix(tables): useMemo deps + placeholder memo initialCharacter check

- Use primitive editingId/editValue in useMemo deps instead of whole
  useInlineRename object (which creates a new ref every render)
- Add initialCharacter comparison to placeholderPropsAreEqual, matching
  the existing pattern in dataRowPropsAreEqual

* fix(tables): address round 2 review comments

- Mirror name validation (regex + max length) in PatchTableSchema so
  validateTableName failures return 400 instead of 500
- Add .returning() + rows-affected check to renameWorkspaceFile,
  matching the renameTable pattern
- Check response.ok before parsing JSON in useRenameWorkspaceFile,
  matching the useRenameTable pattern

* refactor(tables): reuse InlineRenameInput in BreadcrumbSegment

Replace duplicated inline input markup with the shared component.
Eliminates redundant useRef, useEffect, and input boilerplate.

* fix(tables): set doneRef in cancelRename to prevent blur-triggered save

Escape → cancelRename → input unmounts → blur → submitRename would
save instead of canceling. Now cancelRename sets doneRef like
submitRename does, blocking the subsequent blur handler.

* fix(tables): pointercancel cleanup + typed FileConflictError

- Add pointercancel handler to column resize to prevent listener leaks
  when system interrupts the pointer (touch-action override, etc.)
- Replace stringly-typed error.message.includes('already exists') with
  FileConflictError class for refactor-safe 409 status detection

* fix(tables): stable useCallback dep + rename shadowed variable

- Use listRename.startRename (stable ref) instead of whole listRename
  object in handleContextMenuRename deps
- Rename inner 'target' to 'origin' in arrow-key handler to avoid
  shadowing the outer HTMLElement 'target'

* fix(tables): move class below imports, stable submitRename, clear editingCell

- Move FileConflictError below import statements (import-first convention)
- Make submitRename a stable useCallback([]) by reading editingId and
  editValue through refs (matches existing onSaveRef pattern)
- Add setEditingCell(null) to handleEmptyRowClick for symmetry with
  handleCellClick

* feat(tables): persist column widths in table metadata

Column widths now survive navigation and page reloads. On resize-end,
widths are debounced (500ms) and saved to the table's metadata field
via a new PUT /api/table/[tableId]/metadata endpoint. On load, widths
are seeded from the server once via React Query.

* fix type checking for file viewer

* fix(tables): address review feedback — 4 fixes

1. headerRename.onSave now uses the fileId parameter directly instead
   of the selectedFile closure, preventing rename-wrong-file race
2. updateMetadataMutation uses ref pattern matching mutateRef/createRef
3. Type-to-enter filters non-numeric chars for number columns, non-date
   chars for date columns
4. renameValue only passed to actively-renaming ColumnHeaderMenu,
   preserving React.memo for other columns

* fix(tables): position-based gap rows, insert above/below, consistency fixes

- Fix gap row insert shifting: only shift rows when target position is
  occupied, preventing unnecessary displacement of rows below
- Switch to position-based indexing throughout (positionMap, maxPosition)
  instead of array-index for correct sparse position handling
- Add insert row above/below to context menu
- Use CellContent for pending values in PositionGapRows (matching PlaceholderRows)
- Add belowHeader selection overlay logic to PositionGapRows
- Remove unnecessary 500ms debounce on column width persistence

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix cells nav w keyboard

* added preview panel for html, markdown rendering, completed table

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): one small tables ting (#3497)

* feat(exa-hosted-key): Restore exa hosted key (#3499)

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement(ui): consistent styling

* styling alignment

* improvements(tables): styling improvements

* improve resizer for file preview for html files

* updated document icon

* fix(credentials): exclude regular login methods from credential sync

* update docs

* upgrade turbo

* improvement: tables, chat

* Fix table column delete

* small table rename bug, files updates not persisting

* Table batch ops

* fix(credentials): block usage at execution layer without perms + fix invites

* feat(hosted-key-services) Add hosted key for multiple services (#3461)

* feat(hosted keys): Implement serper hosted key

* Handle required fields correctly for hosted keys

* Add rate limiting (3 tries, exponential backoff)

* Add custom pricing, switch to exa as first hosted key

* Add telemetry

* Consolidate byok type definitions

* Add warning comment if default calculation is used

* Record usage to user stats table

* Fix unit tests, use cost property

* Include more metadata in cost output

* Fix disabled tests

* Fix spacing

* Fix lint

* Move knowledge cost restructuring away from generic block handler

* Migrate knowledge unit tests

* Lint

* Fix broken tests

* Add user based hosted key throttling

* Refactor hosted key handling. Add optimistic handling of throttling for custom throttle rules.

* Remove research as hosted key. Recommend BYOK if throtttling occurs

* Make adding api keys adjustable via env vars

* Remove vestigial fields from research

* Make billing actor id required for throttling

* Switch to round robin for api key distribution

* Add helper method for adding hosted key cost

* Strip leading double underscores to avoid breaking change

* Lint fix

* Remove falsy check in favor for explicit null check

* Add more detailed metrics for different throttling types

* Fix _costDollars field

* Handle hosted agent tool calls

* Fail loudly if cost field isn't found

* Remove any type

* Fix type error

* Fix lint

* Fix usage log double logging data

* Fix test

* Add browseruse hosted key

* Add firecrawl and serper hosted keys

* feat(hosted key): Add exa hosted key (#3221)

* feat(hosted keys): Implement serper hosted key

* Handle required fields correctly for hosted keys

* Add rate limiting (3 tries, exponential backoff)

* Add custom pricing, switch to exa as first hosted key

* Add telemetry

* Consolidate byok type definitions

* Add warning comment if default calculation is used

* Record usage to user stats table

* Fix unit tests, use cost property

* Include more metadata in cost output

* Fix disabled tests

* Fix spacing

* Fix lint

* Move knowledge cost restructuring away from generic block handler

* Migrate knowledge unit tests

* Lint

* Fix broken tests

* Add user based hosted key throttling

* Refactor hosted key handling. Add optimistic handling of throttling for custom throttle rules.

* Remove research as hosted key. Recommend BYOK if throtttling occurs

* Make adding api keys adjustable via env vars

* Remove vestigial fields from research

* Make billing actor id required for throttling

* Switch to round robin for api key distribution

* Add helper method for adding hosted key cost

* Strip leading double underscores to avoid breaking change

* Lint fix

* Remove falsy check in favor for explicit null check

* Add more detailed metrics for different throttling types

* Fix _costDollars field

* Handle hosted agent tool calls

* Fail loudly if cost field isn't found

* Remove any type

* Fix type error

* Fix lint

* Fix usage log double logging data

* Fix test

---------

Co-authored-by: Theodore Li <teddy@zenobiapay.com>

* Fail fast on cost data not being found

* Add hosted key for google services

* Add hosting configuration and pricing logic for ElevenLabs TTS tools

* Add linkup hosted key

* Add jina hosted key

* Add hugging face hosted key

* Add perplexity hosting

* Add broader metrics for throttling

* Add skill for adding hosted key

* Lint, remove vestigial hosted keys not implemented

* Revert agent changes

* fail fast

* Fix build issue

* Fix build issues

* Fix type error

* Remove byok types that aren't implemented

* Address feedback

* Use default model when model id isn't provided

* Fix cost default issues

* Remove firecrawl error suppression

* Restore original behavior for hugging face

* Add mistral hosted key

* Remove hugging face hosted key

* Fix pricing mismatch is mistral and perplexity

* Add hosted keys for parallel and brand fetch

* Add brandfetch hosted key

* Update types

* Change byok name to parallel_ai

* Add telemetry on unknown models

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement(settings): SSR prefetch, code splitting, dedicated skeletons

* fix: bust browser cache for workspace file downloads

The downloadFile function was using a plain fetch() that honored the
aggressive cache headers, causing newly created files to download empty.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(settings): use emcn Skeleton in extracted skeleton files

* fix(settings): extract shared response mappers to prevent server/client shape drift

Addresses PR review feedback — prefetch.ts duplicated response mapping logic from client hooks. Extracted mapGeneralSettingsResponse and mapUserProfileResponse as shared functions used by both client fetch and server prefetch.

* update byok page

* fix(settings): include theme sync in client-side prefetch queryFn

Hover-based prefetchGeneralSettings now calls syncThemeToNextThemes, matching the useGeneralSettings hook behavior so theme updates aren't missed when prefetch refreshes stale cache.

* fix(byok): use EMCN Input for search field instead of ui Input

Replace @/components/ui Input with the already-imported EmcnInput for design-system consistency.

* fix(byok): use ui Input for search bar to match other settings pages

* fix(settings): use emcn Input for file input in general settings

* improvement(settings): add search bar to skeleton loading states

Skeletons now include the search bar (and action button where applicable) so the layout matches the final component 1:1. Eliminates layout shift when the dynamic chunk loads — search bar area is already reserved by the skeleton.

* fix(settings): align skeleton layouts with actual component structures

- Fix list item gap from 12px to 8px across all skeletons (API keys, custom tools, credentials, MCP)
- Add OAuth icon placeholder to credential skeleton
- Fix credential button group gap from 8px to 4px
- Remove incorrect gap-[4px] from credential-sets text column
- Rebuild debug skeleton to match real layout (description + input/button row)
- Add scrollable wrapper to BYOK skeleton with more representative item count

* chore: lint fixes

* improvement(sidebar): match workspace switcher popover width to sidebar

Use Radix UI's built-in --radix-popover-trigger-width CSS variable
instead of hardcoded 160px so the popover matches the trigger width
and responds to sidebar resizing.

* revert hardcoded ff

* fix: copilot, improvement: tables, mothership

* feat: inline chunk editor and table batch ops with undo/redo (#3504)

* feat: inline chunk editor and table batch operations with undo/redo

Replace modal-based chunk editing/creation with inline editor following
the files tab pattern (state-based view toggle with ResourceHeader).
Add batch update API endpoint, undo/redo support, and Popover-based
context menus for tables.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove icons from table context menu PopoverItems

Icons were incorrectly carried over from the DropdownMenu migration.
PopoverItems in this codebase use text-only labels.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: restore DropdownMenu for table context menu

The table-level context menu was incorrectly migrated to Popover during
conflict resolution. Only the row-level context menu uses Popover; the
table context menu should remain DropdownMenu with icons, matching the
base branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: bound cross-page chunk navigation polling to max 50 retries

Prevent indefinite polling if page data never loads during
chunk navigation across page boundaries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: navigate to last page after chunk creation for multi-page documents

After creating a chunk, navigate to the last page (where new chunks
append) before selecting it. This prevents the editor from showing
"Loading chunk..." when the new chunk is not on the current page.
The loading state breadcrumb remains as an escape hatch for edge cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add duplicate rowId validation to BatchUpdateByIdsSchema

Adds a .refine() check to reject duplicate rowIds in batch update
requests, consistent with the positions uniqueness check on batch insert.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review comments

- Fix disableEdit logic: use || instead of && so connector doc chunks
  cannot be edited from context menu (row click still opens viewer)
- Add uniqueness validation for rowIds in BatchUpdateByIdsSchema
- Fix inconsistent bg token: bg-background → bg-[var(--bg)] in Pagination

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove duplicate rowId uniqueness refine on BatchUpdateByIdsSchema

The refine was applied both on the inner updates array and the outer
object. Keep only the inner array refine which is cleaner.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address additional PR review comments

- Fix stale rowId after create-row redo: patch undo stack with new row
  ID using patchUndoRowId so subsequent undo targets the correct row
- Fix text color tokens in Pagination: use CSS variable references
  (text-[var(--text-body)], text-[var(--text-secondary)]) instead of
  Tailwind semantic tokens for consistency with the rest of the file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove dead code and fix type errors in table context menu

Remove unused `onAddData` prop and `isEmptyCell` variable from row context
menu (introduced in PR but never wired to JSX). Fix type errors in
optimistic update spreads by removing unnecessary `as Record<string, unknown>`
casts that lost the RowData type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: prevent false "Saved" status on invalid content and mark fire-and-forget goToPage calls

ChunkEditor.handleSave now throws on empty/oversized content instead of
silently returning, so the parent's catch block correctly sets saveStatus
to 'error'. Also added explicit `void` to unawaited goToPage(1) calls
in filter handlers to signal intentional fire-and-forget.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: handle stale totalPages in handleChunkCreated for new-page edge case

When creating a chunk that spills onto a new page, totalPages in the
closure is stale. Now polls displayChunksRef for the new chunk, and if
not found, checks totalPagesRef for an updated page count and navigates
to the new last page before continuing to poll.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Streaming fix -- need to test more

* Make mothership block use long input instead of prompt input

* improvement(billing): isAnnual metadata + docs updates (#3506)

* improvement(billing): on demand toggling and infinite limits

* store stripe metadata to distinguish annual vs monthly

* udpate docs

* address bugbot

* Add piping

* feat(clean-hosted-keys) Remove eleven labs, browseruse. Tweak firecrawl and mistral key impl (#3503)

* Remove eleven labs, browseruse, and firecrawl

* Remove creditsUsed output

* Add back mistral hosting for mistral blocks

* Add back firecrawl since they queue up concurrent requests

* Fix price calculation, remove agent since its super long running and will clog up queue

* Define hosting per tool

* Remove redundant token finding

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* Update vfs to handle hosted keys

* improvement(tables): fix cell editing flash, batch API docs, and UI polish (#3507)

* fix: show text cursor in chunk editor and ensure textarea fills container

Add cursor-text to the editor wrapper so the whole area shows a text
cursor. Click on empty space focuses the textarea. Changed textarea from
h-full/w-full to flex-1/min-h-0 so it properly fills the flex container.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improvement(tables): fix cell editing flash, add batch API docs, and UI polish

Fix stale-data flash when saving inline cell edits by using TanStack Query's
isPending+variables pattern instead of manual cache writes. Also adds OpenAPI
docs for batch table endpoints, DatePicker support in row modal, duplicate row
in context menu, and styling improvements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove dead resolveColumnFromEvent callback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: unify paste undo into single create-rows action

Batch-created rows from paste now push one `create-rows` undo entry
instead of N individual `create-row` entries, so a single Ctrl+Z
reverses the entire paste operation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: validate dates in inline editor and displayToStorage

InlineDateEditor now validates computed values via Date.parse before
saving, preventing invalid strings like "hello" from being sent to the
server. displayToStorage now rejects out-of-range month/day values
(e.g. 13/32) instead of producing invalid YYYY-MM-DD strings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: accept ISO date format in inline date editor

Fall back to raw draft input when displayToStorage returns null, so
valid ISO dates like "2024-03-15" pasted or typed directly are
accepted instead of silently discarded. Date.parse still validates
the final value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add ISO date support to displayToStorage and fix picker Escape

displayToStorage now recognizes YYYY-MM-DD input directly, so ISO
dates typed or pasted work correctly for both saving and picker sync.

DatePicker Escape now refocuses the input instead of saving, so the
user can press Escape again to cancel or Enter to confirm — matching
the expected cancel behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove dead paste boundary check

The totalR guard in handlePaste could never trigger since totalR
included pasteRows.length, making targetRow always < totalR.
Remove the unused variable and simplify the selection focus calc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* update openapi

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix dysfunctional unique operation in tables

* feat(autosave): files and chunk editor autosave with debounce + refetch  (#3508)

* feat(files): debounced autosave while editing

* address review comments

* more comments

* fix: unique constraint check crash and copilot table initial rows

- Fix TypeError in updateColumnConstraints: db.execute() returns a
  plain array with postgres-js, not { rows: [...] }. The .rows.length
  access always crashed, making "Set unique" completely broken.

- Add initialRowCount: 20 to copilot table creation so tables created
  via chat have the same empty rows as tables created from the UI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix signaling

* revert: remove initialRowCount from copilot table creation

Copilot populates its own data after creating a table, so pre-creating
20 empty rows causes data to start at position 21 with empty rows above.
initialRowCount only makes sense for the manual UI creation flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improvement: chat, workspace header

* chat metadata

* Fix schema mismatch (#3510)

Co-authored-by: Theodore Li <theo@sim.ai>

* Fixes

* fix: manual table creation starts with 1 row, 1 column

Manual tables now create with a single 'name' column and 1 row instead
of 2 columns and 20 rows. Copilot tables remain at 0 rows, 0 columns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: horizontal scroll in embedded table by replacing overflow-hidden with overflow-clip

Cell content spans used Tailwind's `truncate` (overflow: hidden), creating
scroll containers that consumed trackpad wheel events on macOS without
propagating to the actual scroll ancestor. Replaced with overflow-clip
which clips identically but doesn't create a scroll container. Also moved
focus target from outer container to the scroll div for correctness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix tool call ordering

* Fix tests

* feat: add task multi-select, context menu, and subscription UI updates

Add shift-click range selection, cmd/ctrl-click toggle, and right-click
context menu for tasks in sidebar matching workflow/folder patterns.
Update subscription settings tab UI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(credentials): autosync behaviour cross workspace (#3511)

* fix(credentials): autosync behaviour cross workspace

* address comments

* fix(api-key-reminder) Add reminder on hosted keys that api key isnt needed (#3512)

* Add reminder on hosted keys that api key isnt needed

* Fix test case

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement: sidebar, chat

* Usage limit

* Plan prompt

* fix(sidebar): workspace header collapse

* fix(sidebar): task navigation

* Subagent tool call persistence

* Don't drop suabgent text

* improvement(ux): streaming

* improvement: thinking

* fix(random): optimized kb connector sync engine, rerenders in tables, files, editors, chat (#3513)

* optimized kb connector sync engine, rerenders in tables, files, editors, chat

* refactor(sidebar): rename onTaskClick to onMultiSelectClick for clarity

Made-with: Cursor

* ack comments, add docsFailed

* feat(email-footer) Add "sent with sim ai" for free users (#3515)

* Add "sent with sim ai" for free users

* Only add prompt injection on free tier

* Add try catch around billing info fetch

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement: modals

* ran migrations

* fix(mothership): fix hardcoded workflow color, tables drag line overflowing

* feat(mothership): file attachment indicators, persistence, and chat input improvements

- Show image thumbnails and file-icon cards above user messages in mothership chat
- Persist file attachment metadata (key, filename, media_type, size) in DB with user messages
- Restore attachments from history via /api/files/serve/ URLs so they survive refresh/navigation
- Unify all chat file inputs to use shared CHAT_ACCEPT_ATTRIBUTE constant
- Fix file thumbnail overflow: use flex-wrap instead of hidden horizontal scroll
- Compact attachment cards in floating workflow chat messages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improvement: search modal

* improvement(usage): free plan to 1000 credits  (#3516)

* improvement(billing): free plan to five dollars

* fix comment

* remove per month terminology from marketing

* generate migration

* remove migration

* add migration back

* feat(workspace): add workspace color changing, consolidate update hooks, fix popover dismiss

- Add workspace color change via context menu, reusing workflow ColorGrid UI
- Consolidate useUpdateWorkspaceName + useUpdateWorkspaceColor into useUpdateWorkspace
- Fix popover hover submenu dismiss by using DismissableLayerBranch with pointerEvents
- Remove passthrough wrapper for export, reuse Workspace type for capturedWorkspaceRef
- Reorder log columns: workflow first, merge date+time into single column

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update oauth cred tool

* fix(diff-controls): fixed positioning for copilot diff controls

* fix(font): added back old font for emcn code editor

* improvement: panel, special tags

* improvement: chat

* improvement: loading and file dropping

* feat(templates): create home templates

* fix(uploads): resolve .md file upload rejection and deduplicate file type utilities

Browsers report empty or application/octet-stream MIME types for .md files,
causing copilot uploads to be rejected. Added resolveFileType() utility that
falls back to extension-based MIME resolution at both client and server
boundaries. Consolidated duplicate MIME mappings into module-level constants,
removed duplicate isImageFileType from copilot module, and replaced hardcoded
ALLOWED_EXTENSIONS with composition from shared validation constants. Also
switched file attachment previews to use shared getDocumentIcon utility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(home): prevent initial view from being scrollable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* autofill fixes

* added back integrations page, reverted secrets page back to old UI

* Fix workspace dropdown getting cut off when sidebar is collapsed

* fix(mothership): lint (#3517)

* fix(mothership): lint

* fix typing

* fix tests

* fix stale query

* fix plan display name

* Feat/add mothership manual workflow runs (#3520)

* Add run and open workflow buttons in workflow preview

* Send log request message after manual workflow run

* Make edges in embedded workflow non-editable

* Change chat to pass in log as additional context

* Revert "Change chat to pass in log as additional context"

This reverts commit e957dffb2f.

* Revert "Send log request message after manual workflow run"

This reverts commit 0fb92751f0.

* Move run and workflow icons to tab bar

* Simplify boolean condition

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* feat(resource-tab-scroll): Allow vertical scrolling to scroll resource tab

* fix(remove-speed-hosted-key) Remove maps speed limit hosted key, it's deprecated (#3521)

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement: home, sidebar

* fix(download-file): render correct file download link for mothership (#3522)

* fix(download-file): render correct file download link for mothership

* Fix uunecessary call

* Use simple strip instead of db lookup and moving behavior

* Make regex strip more strict

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement: schedules, auto-scroll

* fix(settings): navigate back to origin page instead of always going home

Use sessionStorage to store the return URL when entering settings, and
use router.replace for tab switches so history doesn't accumulate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(schedules): release lastQueuedAt lock on all exit paths to prevent stuck schedules

Multiple error/early-return paths in executeScheduleJob and executeJobInline
were exiting without clearing lastQueuedAt, causing the dueFilter to permanently
skip those schedules — resulting in stale "X hours ago" display for nextRunAt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(mothership): inline rename for resource tabs + workspace_file rename tool

- Add double-click inline rename on file and table resource tabs
- Wire useInlineRename + useRenameWorkspaceFile/useRenameTable mutations
- Add rename operation to workspace_file copilot tool (schema, server, router)
- Add knowledge base resource support (type, extraction, rendering, actions)
- Accept optional className on InlineRenameInput for context-specific sizing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: remove inline rename UI from resource tabs

Keep the workspace_file rename tool for the mothership agent.
Only the UI-side inline rename (double-click tabs) is removed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(mothership): knowledge base resource extraction + Resource/ResourceTable refactor

- Extract KB resources from knowledge subagent respond format (knowledge_bases array)
- Add knowledge_base tool to RESOURCE_TOOL_NAMES and TOOL_UI_METADATA
- Extract ResourceTable as independently composable memoized component
- Move contentOverride/overlay to Resource shell level (not table primitive)
- Remove redundant disableHeaderSort and loadingRows props
- Rename internal sort state for clarity (sort → internalSort, sortOverride → externalSort)
- Export ResourceTable and ResourceTableProps from barrel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(logs) Run workflows client side in mothership to transmit logs (#3529)

* Run workflows client side in mothership to transmit logs

* Initialize set as constant, prevent duplicate execution

* Fix lint

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* fix(import) fix missing file

* fix(resource): Hide resources that have been deleted (#3528)

* Hide resources that have been deleted

* Handle table, workflow not found

* Add animation to prevent flash when previous resource was deleted

* Fix animation playing on every switch

* Run workflows client side in mothership to transmit logs

* Fix race condition for animation

* Use shared workflow tool util file

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* fix: chat scrollbar on sidebar collapse/open

* edit existing workflow should bring up artifact

* fix(agent) subagent and main agent text being merged without spacing

* feat(mothership): remove resource-level delete tools from copilot

Remove delete operations for workflows, folders, tables, and files
from the mothership copilot to prevent destructive actions via AI.
Row-level and column-level deletes are preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: stop sidebar from auto-collapsing when resource panel appears (#3540)

The sidebar was forcibly collapsed whenever a resource (e.g. workflow)
first appeared in the resource panel during a task. This was disruptive
on larger screens where users want to keep both the sidebar and resource
panel visible simultaneously.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(mothership): insert copilot-created workflows at top of list (#3537)

* feat(mothership): remove resource-level delete tools from copilot

Remove delete operations for workflows, folders, tables, and files
from the mothership copilot to prevent destructive actions via AI.
Row-level and column-level deletes are preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(mothership): insert copilot-created workflows at top of list

* fix(mothership): server-side top-insertion sort order and deduplicate registry logic

* fix(mothership): include folder sort orders when computing top-insertion position

* fix(mothership): use getNextWorkflowColor instead of hardcoded color

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(stop) Add stop of motehership ran workflows, persist stop messages (#3538)

* Connect play stop workflow in embedded view to workflow

* Fix stop not actually stoping workflow

* Fix ui not showing stopped by user

* Lint fix

* Plumb cancellation through system

* Stopping mothership chat stops workflow

* Remove extra fluff

* Persist blocks on cancellation

* Add root level stopped by user

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* fix(autolayout): targetted autolayout heuristic restored (#3536)

* fix(autolayout): targetted autolayout heuristic restored

* fix autolayout boundary cases

* more fixes

* address comments

* on conflict updates

* address more comments

* fix relative position scope

* fix tye omission

* address bugbot comment

* Credential tags

* Credential id field

* feat(mothership): server-persisted unread task indicators via SSE (#3549)

* feat(mothership): server-persisted unread task indicators via SSE

Replace fragile client-side polling + timer-based green flash with
server-persisted lastSeenAt semantics, real-time SSE push via Redis
pub/sub, and dot overlay UI on the Blimp icon.

- Add lastSeenAt column to copilotChats for server-persisted read state
- Add Redis/local pub/sub singleton for task status events (started,
  completed, created, deleted, renamed)
- Add SSE endpoint (GET /api/mothership/events) with heartbeat and
  workspace-scoped filtering
- Add mark-read endpoint (POST /api/mothership/chats/read)
- Publish SSE events from chat, rename, delete, and auto-title handlers
- Add useTaskEvents hook for client-side SSE subscription
- Add useMarkTaskRead mutation with optimistic update
- Replace timer logic in sidebar with TaskStatus state machine
  (running/unread/idle) and dot overlay using brand color variables
- Mark tasks read on mount and stream completion in home page
- Fix security: add userId check to delete WHERE clause
- Fix: bump updatedAt on stream completion
- Fix: set lastSeenAt on rename to prevent false-positive unread

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review feedback

- Return 404 when delete finds no matching chat (was silent no-op)
- Move log after ownership check so it only fires on actual deletion
- Publish completed SSE event from stop route so sidebar dot clears on abort

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: backfill last_seen_at in migration to prevent false unread dots

Existing rows would have last_seen_at = NULL after migration, causing
all past completed tasks to show as unread. Backfill sets last_seen_at
to updated_at for all existing rows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: timestamp mismatch on task creation + wasSendingRef leak across navigation

- Pass updatedAt explicitly alongside lastSeenAt on chat creation so
  both use the same JS timestamp (DB defaultNow() ran later, causing
  updatedAt > lastSeenAt → false unread)
- Reset wasSendingRef when chatId changes to prevent a stale true
  from task A triggering a redundant markRead on task B

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: mark-read fires for inline-created chats + encode workspaceId in SSE URL

Expose resolvedChatId from useChat so home.tsx can mark-read even when
chatId prop stays undefined after replaceState URL update. Also
URL-encode workspaceId in EventSource URL as a defensive measure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: auto-focus home input on initial view + fix sidebar task click handling

Auto-focus the textarea when the initial home view renders. Also fix
sidebar task click to always call onMultiSelectClick so selection state
stays consistent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: auto-title sets lastSeenAt + move started event inside DB guard

Auto-title now sets both updatedAt and lastSeenAt (matching the rename
route pattern) to prevent false-positive unread dots. Also move the
'started' SSE event inside the if(updated) guard so it only fires when
the DB update actually matched a row.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* modified tasks multi select to be just like workflows

* fix

* refactor: extract generic pub/sub and SSE factories + fixes

- Extract createPubSubChannel factory (lib/events/pubsub.ts) to eliminate
  duplicated Redis/EventEmitter boilerplate between task and MCP pub/sub
- Extract createWorkspaceSSE factory (lib/events/sse-endpoint.ts) to share
  auth, heartbeat, and cleanup logic across SSE endpoints
- Fix auto-title race suppressing unread status by removing updatedAt/lastSeenAt
  from title-only DB update
- Fix wheel event listener leak in ResourceTabs (RefCallback cleanup was silently
  discarded)
- Fix getFullSelection() missing taskIds (inconsistent with hasAnySelection)
- Deduplicate SSE_RESPONSE_HEADERS to spread from shared SSE_HEADERS
- Hoist isSttAvailable to module-level constant to avoid per-render IIFE

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat(logs): add workflow trigger type for sub-workflow executions (#3554)

* feat(logs): add workflow trigger type for sub-workflow executions

* fix(logs): align workflow filter color with blue-secondary badge variant

* feat(tab) allow user to control resource tabs

* Make resources persist to backend

* Use colored squares for workflows

* Add click and drag functionality to resource

* Fix expanding panel logic

* Reduce duplication, reading resource also opens up resource panel

* Move resource dropdown to own file

* Handle renamed resources

* Clicking already open tab should just switch to tab

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* Fix new resource tab button not appearing on tasks

* improvement(ui): dropdown menus, icons, globals

* improvement: notifications, terminal, globals

* reverted task logic

* feat(context) pass resource tab as context (#3555)

* feat(context) add currenttly open resource file to context for agent

* Simplify resource resolution

* Skip initialize vfs

* Restore ff

* Add back try catch

* Remove redundant code

* Remove json serialization/deserialization loop

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* Feat(references) add at to reference sim resources(#3560)


* feat(chat) add at sign

* Address bugbot issues

* Remove extra chatcontext defs

* Add table and file to schema

* Add icon to chip for files

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* improvement(refactor): move to soft deletion of resources + reliability improvements (#3561)

* improvement(deletion): migrate to soft deletion of resources

* progress

* scoping fixes

* round of fixes

* deduplicated name on workflow import

* fix tests

* add migration

* cleanup dead code

* address bugbot comments

* optimize query

* feat(sim-mailer): email inbox for mothership with chat history and plan gating (#3558)

* feat(sim-mailer): email inbox for mothership with chat history and plan gating

* revert hardcoded ff

* fix(inbox): address PR review comments - plan enforcement, idempotency, webhook auth

- Enforce Max plan at API layer: hasInboxAccess() now checks subscription tier (>= 25k credits or enterprise)
- Add idempotency guard to executeInboxTask() to prevent duplicate emails on Trigger.dev retries
- Add AGENTMAIL_WEBHOOK_SECRET env var for webhook signature verification (Bearer token)

* improvement(inbox): harden security and efficiency from code audit

- Use crypto.timingSafeEqual for webhook secret comparison (prevents timing attacks)
- Atomic claim in executor: WHERE status='received' prevents duplicate processing on retries
- Parallelize hasInboxAccess + getUserEntityPermissions in all API routes (reduces latency)
- Truncate email body at webhook insertion (50k char limit, prevents unbounded DB storage)
- Harden escapeAttr with angle bracket and single quote escaping
- Rename use-inbox.ts to inbox.ts (matches hooks/queries/ naming convention)

* fix(inbox): replace Bearer token auth with proper Svix HMAC-SHA256 webhook verification

- Use per-workspace webhook secret from DB instead of global env var
- Verify AgentMail/Svix signatures: HMAC-SHA256 over svix-id.timestamp.body
- Timing-safe comparison via crypto.timingSafeEqual
- Replay protection via timestamp tolerance (5 min window)
- Join mothershipInboxWebhook in workspace lookup (zero additional DB calls)
- Remove dead AGENTMAIL_WEBHOOK_SECRET env var
- Select only needed workspace columns in webhook handler

* fix(inbox): require webhook secret — reject requests when secret is missing

Previously, if the webhook secret was missing from the DB (corrupted state),
the handler would skip verification entirely and process the request
unauthenticated. Now all three conditions are hard requirements: secret must
exist in DB, Svix headers must be present, and signature must verify.

* fix(inbox): address second round of PR review comments

- Exclude rejected tasks from rate limit count to prevent DoS via spam
- Strip raw HTML from LLM output before marked.parse to prevent XSS in emails
- Track responseSent flag to prevent duplicate emails when DB update fails after send

* fix(inbox): address third round of PR review comments

- Use dynamic isHosted from feature-flags instead of hardcoded true
- Atomic JSON append for chat message persistence (eliminates read-modify-write race)
- Handle cutIndex === 0 in stripQuotedReply (body starts with quote)
- Clean up orphan mothershipInboxWebhook row on enableInbox rollback
- Validate status query parameter against enum in tasks API

* fix(inbox): validate cursor param, preserve code blocks in HTML stripping

- Validate cursor date before using in query (return 400 for invalid)
- Split on fenced code blocks before stripping HTML tags to preserve
  code examples in email responses

* fix(inbox): return 500 on webhook server errors to enable Svix retries

* fix(inbox): remove isHosted guard from hasInboxAccess — feature flag is sufficient

* fix(inbox): prevent double-enable from deleting webhook secret row

* fix(inbox): null-safe stripThinkingTags, encode URL params, surface remove-sender errors

- Guard against null result.content in stripThinkingTags
- Use encodeURIComponent on all AgentMail API path parameters
- Surface handleRemoveSender errors to the user instead of swallowing

* improvement(inbox): remove unused types, narrow SELECT queries, fix optimistic ID collision

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(inbox): add keyboard accessibility to clickable task rows

* fix(inbox): use Svix library for webhook verification, fix responseSent flag, prevent inbox enumeration

- Replace manual HMAC-SHA256 verification with official Svix library per AgentMail docs
- Fix responseSent flag: only set true when email delivery actually succeeds
- Return consistent 401 for unknown inbox and bad signature to prevent enumeration
- Make AgentMailInbox.organization_id optional to match API docs

* chore(db): rebase inbox migration onto feat/mothership-copilot (0172 → 0173)

Sync schema with target branch and regenerate migration as 0173
to avoid conflicts with 0172_silky_magma on feat/mothership-copilot.

* fix(db): rebase inbox migration to 0173 after feat/mothership-copilot divergence

Target branch added 0172_silky_magma, so our inbox migration is now 0173_youthful_stryfe.

* fix(db): regenerate inbox migration after rebase on feat/mothership-copilot

* fix(inbox): case-insensitive email match and sanitize javascript: URIs in email HTML

- Use lower() in isSenderAllowed SQL to match workspace members regardless
  of email case stored by auth provider
- Strip javascript:, vbscript:, and data: URIs from marked HTML output to
  prevent XSS in outbound email responses

* fix(inbox): case-insensitive email match in resolveUserId

Consistent with the isSenderAllowed fix — uses lower() so mixed-case
stored emails match correctly, preventing silent fallback to workspace owner.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Kb args

* refactor(resource): remove logs-specific escape hatches from Resource abstraction

Logs now composes ResourceHeader + ResourceOptionsBar + ResourceTable directly
instead of using Resource with contentOverride/overlay escape hatches. Removes
contentOverride, onLoadMore, hasMore, isLoadingMore from ResourceProps. Adds
ColumnOption to barrel export and fixes table.tsx internal import.

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

* fix(sim-mailer): download email attachments and pass to LLM as multimodal content

Attachments were only passed as metadata text in the email body. Now downloads
actual file bytes from AgentMail, converts via createFileContent (same path as
interactive chat), and sends as fileAttachments to the orchestrator. Also
parallelizes attachment fetching with workspace context loading, and downloads
multiple attachments concurrently via Promise.allSettled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(connector): add Gmail knowledge base connector with thread-based sync and filtering

Syncs email threads from Gmail into knowledge bases with configurable filters:
label scoping, date range presets, promotions/social exclusion, Gmail search
syntax support, and max thread caps to keep KB size manageable.

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

* feat(connector): add Outlook knowledge base connector with conversation grouping and filtering

Syncs email conversations from Outlook/Office 365 via Microsoft Graph API.
Groups messages by conversationId into single documents. Configurable filters:
folder selection, date range presets, Focused Inbox, KQL search syntax, and
max conversation caps.

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

* cleanup resource definition

* feat(connectors): add 8 knowledge base connectors — Zendesk, Intercom, ServiceNow, Google Sheets, Microsoft Teams, Discord, Google Calendar, Reddit

Each connector syncs documents into knowledge bases with configurable filtering:

- Zendesk: Help Center articles + support tickets with status/locale filters
- Intercom: Articles + conversations with state filtering
- ServiceNow: KB articles + incidents with state/priority/category filters
- Google Sheets: Spreadsheet tabs as LLM-friendly row-by-row documents
- Microsoft Teams: Channel messages (Slack-like pattern) via Graph API
- Discord: Channel messages with bot token auth
- Google Calendar: Events with date range presets and attendee metadata
- Reddit: Subreddit posts with top comments, sort/time filters

All connectors validated against official API docs with bug fixes applied.

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

* fix(inbox): fetch real attachment binary from presigned URL and persist for chat display

The AgentMail attachment endpoint returns JSON metadata with a download_url,
not raw binary. We were base64-encoding the JSON text and sending it to the
LLM, causing provider rejection. Now we parse the metadata, fetch the actual
file from the presigned URL, upload it to copilot storage, and persist it on
the chat message so images render inline with previews.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* added agentmail domain for mailer

* added docs for sim mailer

* fix(resource) handle resource deletion  deletion (#3568)

* Add handle dragging tab to input chat

* Add back delete tools

* Handle deletions properly with resources view

* Fix lint

* Add permisssions checking

* Skip resource_added event when resource is deleted

* Pass workflow id as context

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* update docs styling, add delete confirmation on inbox

* Fix fast edit route

* updated docs styling, added FAQs, updated content

* upgrade turbo

* fix(knowledge) use consistent empty state for documents page

Replace the centered "No documents yet" text with the standard Resource
table empty state (column headers + create row), matching all other
resource pages. Move "Upload documents" from header action to table
create row as "New documents".

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

* fix(notifications): polish modal styling, credential display, and trigger filters (#3571)

* fix(notifications): polish modal styling, credential display, and trigger filters

- Show credential display name instead of raw account ID in Slack account selector
- Fix label styling to use default Label component (text-primary) for consistency
- Fix modal body spacing with proper top padding after tab bar
- Replace list-card skeleton with form-field skeleton matching actual layout
- Replace custom "Select a Slack account first" box with disabled Combobox (dependsOn pattern)
- Use proper Label component in WorkflowSelector with consistent gap spacing
- Add overflow badge pattern (slice + +N) to level and trigger filter badges
- Use dynamic trigger options from getTriggerOptions() instead of hardcoded CORE_TRIGGER_TYPES
- Relax API validation to accept integration trigger types (z.string instead of z.enum)
- Deduplicate account rows from credential leftJoin in accounts API
- Extract getTriggerOptions() to module-level constants to avoid per-render calls

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

* fix(notifications): address PR review feedback

- Restore accountId in displayName fallback chain (credentialDisplayName || accountId || providerId)
- Add .default([]) to triggerFilter in create schema to preserve backward compatibility
- Treat empty triggerFilter as "match all" in notification matching logic
- Remove unreachable overflow badge for levelFilter (only 2 possible values)

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

---------

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

* fix(settings): add spacing to Sim Keys toggle and replace Sim Mailer icon with Send

Add 24px top margin to the "Allow personal Sim keys" toggle so it doesn't
sit right below the empty state. Replace the Mail envelope icon for Sim
Mailer with a new Send (paper plane) icon matching the emcn icon style.

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

* standardize back buttons in settings

* feat(restore) Add restore endpoints and ui (#3570)

* Add restore endpoints and ui

* Derive toast from notification

* Auth user if workspaceid not found

* Fix recently deleted ui

* Add restore error toast

* Fix deleted at timestamp mismatch

---------

Co-authored-by: Theodore Li <theo@sim.ai>

* fix type errors

* Lint

* improvements: ui/ux around mothership

* reactquery best practices, UI alignment in restore

* clamp logs panel

* subagent thinking text

* fix build, speedup tests by up to 40%

* Fix fast edit

* Add download file shortcut on mothership file view

* fix: SVG file support in mothership chat and file serving

- Send SVGs as document/text-xml to Claude instead of unsupported
  image/svg+xml, so the mothership can actually read SVG content
- Serve SVGs inline with proper content type and CSP sandbox so
  chat previews render correctly
- Add SVG preview support in file viewer (sandboxed iframe)
- Derive IMAGE_MIME_TYPES from MIME_TYPE_MAPPING to reduce duplication
- Add missing webp to contentTypeMap, SAFE_INLINE_TYPES, binaryExtensions
- Consolidate PREVIEWABLE_EXTENSIONS into preview-panel exports

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

* fix: replace image/* wildcard with explicit supported types in file picker

The image/* accept attribute allowed users to select BMP, TIFF, HEIC,
and other image types that are rejected server-side. Replace with the
exact set of supported image MIME types and extensions to match the
copilot upload validation.

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

* Context tags

* Fix lint

* improvement: chat and terminal

---------

Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Theodore Li <teddy@zenobiapay.com>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Theodore Li <theodoreqili@gmail.com>
Co-authored-by: Theodore Li <theo@sim.ai>
2026-03-13 21:02:08 -07:00
79bb4e5ad8 feat(docs): add API reference with OpenAPI spec and auto-generated endpoint pages (#3388)
* feat(docs): add API reference with OpenAPI spec and auto-generated endpoint pages

* multiline curl

* random improvements

* cleanup

* update docs copy

* fix build

* cast

* fix builg

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
2026-03-01 22:53:18 -08:00
Waleed ab48787422 chore(deps): upgrade next.js from 16.1.0-canary.21 to 16.1.6 (#3254) 2026-02-18 16:25:28 -08:00
Waleed 71130c8b0a improvement(monorepo): added tsconfig package, resolved type errors in testing package (#2613) 2025-12-28 00:36:48 -08:00
Waleed d79696beae feat(docs): added vector search (#2583)
* feat(docs): added vector search

* ack comments
2025-12-25 11:00:57 -08:00
Waleed 731997f768 fix(envvars): cleanup unused envvars (#2436)
* fix(envvars): cleanup unused envvars

* removed unused react-google-drive-picker dep
2025-12-17 17:13:01 -08:00
Waleed 7b5405e968 feat(vertex): added vertex to list of supported providers (#2430)
* feat(vertex): added vertex to list of supported providers

* added utils files for each provider, consolidated gemini utils, added dynamic verbosity and reasoning fetcher
2025-12-17 14:57:58 -08:00
Waleed a45bb1bf3b fix(rce): add 'isolate' to list of trusted deps, fixed custom tools environment resolution (#2387)
* fix(rce): add isolate to list of trusted deps

* updated error enchancer in RCE

* fixed

* fix build

* fix failing test

* fix build

* fix build

* remove extraneous comment
2025-12-15 15:24:11 -08:00
Waleed 4da5dd7f74 fix(nextjs): upgrade nextjs to patch security vuln (#2320) 2025-12-11 14:58:31 -08:00
Waleed d06b360b1d fix(docs): fix copy page button and header hook (#2284) 2025-12-09 21:58:54 -08:00
Waleed dcbdcb43aa chore(deps): upgrade to nextjs 16 (#2203)
* chore(deps): upgrade to nextjs 16

* upgraded fumadocs

* ensure vercel uses bun

* fix build

* fix bui;d

* remove redundant vercel.json
2025-12-04 17:55:37 -08:00
Waleed 6d4ba6d5cf chore(deps): upgrade from nextjs 15.4.1 to 15.4.8 and upgrade turborepo (#2180) 2025-12-04 00:20:50 -08:00
Waleedandwaleedlatif1 5d791cd55f feat(i18n): update translations (#2178)
* feat(i18n): update translations

* memory optimizations

---------

Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2025-12-04 00:18:21 -08:00
Waleed f9e822f6c8 feat(docs): added docs analytics drizzle ods (#1957)
* feat(docs): added docs analytics drizzle ods

* fix build
2025-11-12 17:51:58 -08:00
Waleed 4adbae03e7 chore(deps): update fumadocs (#1525) 2025-10-01 20:28:12 -07:00
Vikhyath Mondreti 6101493f12 fix(next-js): pin version (#1358)
* fix(next-js): pin version

* fix
2025-09-16 21:50:52 -07:00
Waleed ab97ac5a77 fix(build): upgrade fumadocs to latest (#1341)
* update infra and remove railway

* fix(builds): upgraded fumadocs

* Revert "update infra and remove railway"

This reverts commit abfa2f8d51.
2025-09-15 19:28:07 -07:00
Waleed ac8bf96eee fix(build): upgrade fumadocs (#1340)
* update infra and remove railway

* fix(build): fixed the build

* Revert "update infra and remove railway"

This reverts commit abfa2f8d51.
2025-09-15 17:47:34 -07:00
Waleed Latif 7e4669108f feat(build): added turbopack builds to prod (#630)
* added turbopack to prod builds

* block access to sourcemaps

* revert changes to docs
2025-07-07 19:51:39 -07:00
Waleed Latif 334f12c602 improvement(docs): add analytics 2025-05-26 13:34:22 -07:00
dependabot[bot] 3f468dc302 chore(deps): bump lucide-react (#415)
Bumps the docs-dependencies group in /apps/docs with 1 update: [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react).


Updates `lucide-react` from 0.479.0 to 0.511.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/0.511.0/packages/lucide-react)

---
updated-dependencies:
- dependency-name: lucide-react
  dependency-version: 0.511.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docs-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-25 18:57:22 -07:00
Waleed LatifandAditya Tripathi 8c268e23dd chore(biome): removed prettier, added biome (#407)
* chore: replace prettier with biome and add linting

* chore: update devcontainer settings to use biome for linting and remove eslint, prettier

* chore: update docker-compose to use Postgres 17-alpine and standardize quotes

* chore: fixed more BUT disabled most rules due to limit

* added additional rules, fixed linting & ts errors

* added additional rules

* rebased & linted

* fixed oauth

* updated biome & minor modifications

---------

Co-authored-by: Aditya Tripathi <aditya@climactic.co>
2025-05-24 03:11:38 -07:00
Waleed Latif 47090713ef Revert "feat(package): add tsconfig pkg to share across packages in monorepo …" (#381)
This reverts commit f5cbcfb514.
2025-05-19 17:03:54 -07:00
Waleed Latif f5cbcfb514 feat(package): add tsconfig pkg to share across packages in monorepo (#380)
* add tsconfig pkg to share across packages in monorepo

* fixed docs build

* acknowledged PR comments
2025-05-19 16:58:51 -07:00
Waleed LatifandAditya Tripathi 717e17d02a feat(bun): upgrade to bun, reduce docker image size by 95%, upgrade docs & ci (#371)
* migrate to bun

* added envvars to drizzle

* upgrade bun devcontainer feature to a valid one

* added bun, docker not working

* updated envvars, updated to bunder and esnext modules

* fixed build, reinstated otel

* feat: optimized multi-stage docker images

* add coerce for boolean envvar

* feat: add docker-compose configuration for local LLM services and remove legacy Dockerfile and entrypoint script

* feat: add docker-compose files for local and production environments, and implement GitHub Actions for Docker image build and publish

* refactor: remove unused generateStaticParams function from various API routes and maintain dynamic rendering

* cleanup

* upgraded bun

* updated ci

* fixed build

---------

Co-authored-by: Aditya Tripathi <aditya@climactic.co>
2025-05-18 01:01:32 -07:00
Waleed Latif a92ee8bf46 feat(turbo): restructured repo to be a standard turborepo monorepo (#341)
* added turborepo

* finished turbo migration

* updated gitignore

* use dotenv & run format

* fixed error in docs

* remove standalone deployment in prod

* fix ts error, remove ignore ts errors during build

* added formatter to the end of the docs generator
2025-05-09 21:45:49 -07:00