Commit Graph

14294 Commits

Author SHA1 Message Date
Guilhem Lemouel 5cc4cd55df fix(frontend): tidy the resource drawer around the Supabase entry point
Connect Supabase was a hand-styled anchor carrying Supabase's brand hex values
rather than a Button, and it sat in a row whose other controls had settled on
unifiedSize md. Making it a Button meant SupabaseIcon had to satisfy IconType, so it
now takes `size` (deriving height/width from it) alongside the string props its other
callers pass.

The manual resource form spaced every field 32px apart and WhitelistIp added another
16px of its own, which read as a gap rather than a rhythm. One gap of 16px, with the
form itself given a little more separation from the description above it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 18:47:31 +02:00
Guilhem Lemouel bc18ebec16 refactor(frontend): one setup checklist and one Supabase step for every host
The data table wizard, the instance database modal and the resource drawer each had
their own version of the same two interactions, and they had already begun to drift:
the wizard's Supabase resource shape was rebuilt by hand in the drawer, and the
instance checks rendered with no notion of a step being in flight.

SetupChecklist replaces LoggedWizardResult, whose only consumer was the instance
modal. It adds the running state that component lacked, so a list driven by an
endpoint that reports nothing until it returns still shows where it is. Both the
instance checks and the Supabase provisioning stages render through it.

SupabaseProjectStep owns picking or creating a project, and useSupabaseOauth owns
the popup leg. Each host keeps only what is genuinely its own: the wizard saves a
variable and resource then verifies the connection, the resource drawer fills in its
own form. Both trigger authorization themselves, so a host can offer it a screen
earlier than the step does.

The lists load behind a spinner because which mode to open on depends on whether the
account has projects; deciding that after rendering flipped the toggle under the user.

Adds a kitchen_sink playground for the checklist so the animation and every failure
position can be exercised without a backend, a superadmin, or a Supabase account.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 18:47:22 +02:00
Guilhem Lemouel 521b9b5720 fix(frontend): scope the connection check to the choice that produced it
A failed check stayed on screen when the user switched Supabase mode or picked a
different provider, so a fresh tab opened showing an error about a database it had
nothing to do with. Clear the report and the error on both switches; re-clicking the
tab already selected leaves an error the user is reading in place.

Also polish the Supabase step: project cards get the provider-card treatment (icon,
p-3, flex column) instead of a hand-rolled variant whose block layout left more
padding above the name than below; form labels settle on text-emphasis; and the
signup link sits under the primary button for anyone who does not have an account
yet.

Drop the "free" badge and the "Free on Supabase" line -- every option in the wizard
is free, so neither told the user anything -- and say what the Supabase card
actually does now that connecting an existing project is the default.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 11:16:10 +02:00
Guilhem Lemouel 97ffd5a72a fix(frontend): run the Supabase OAuth leg in a popup
A full-page redirect unmounts the wizard, so anything the user does on Supabase's
side -- signing in, confirming an email, browsing their dashboard -- leaves them
with nothing pointing back at Windmill, and the wizard had to park its state in
sessionStorage to survive the trip.

Open the connect endpoint in a popup instead. The modal stays on screen throughout
and the callback hands the token back through postMessage rather than navigating.
The parked-state path stays as the fallback for browsers that block the popup.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:51:25 +02:00
Guilhem Lemouel b6a011f9e0 fix(frontend): only offer Supabase when its OAuth client is configured
The wizard offered the Supabase card unconditionally, so on an instance whose
superadmin never configured a supabase_wizard client -- or whose backend is built
without the oauth2 feature, which compiles the whole /api/oauth router out -- the
card dead-ended at a 404. Gate it on listOauthConnects, the same check
ApiConnectForm already makes, fetched on open so configuring the client mid-session
does not require a reload.

Also drop the Supabase project ref from the existing-project cards: it is an opaque
identifier that means nothing outside Supabase's own dashboard URLs. Show the region
instead, plus a status word when the project is not healthy, since a paused project
is the one case where the connection check fails for a reason unrelated to the
password.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 09:48:22 +02:00
Guilhem Lemouel 12b26372ed fix(frontend): simplify Alert layout and balance its vertical padding
The body was rendered by two near-duplicate branches, each wrapping the text in an
extra div only to hang a margin on it, and the margins disagreed: the collapsible
branch spaced above with mt-2, the static one below with mb-2. Since isCollapsed
defaults to true, every non-collapsible alert took the static branch, so titled
alerts read as 24px of space below the text against 16px above -- visibly
off-centre -- with the title and body flush against each other.

Collapse both branches into one and drop the margins; the container's own padding
now sets top and bottom equally, with a small gap under the title row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 09:48:15 +02:00
Guilhem Lemouel ef8141f260 fix: address review findings on the data table wizard
- The Supabase create branch advanced on `provisioning === 4` without consulting
  the check it had just run, so a role that cannot create tables could reach
  Finish. It now blocks and offers Try again.
- Retrying no longer mints a fresh secret variable + resource each time: the
  credentials are only re-created when the password actually changed.
- The generated password is captured before the create call rather than after,
  since a throw there can still leave a project behind.
- On a failed provision the project list is refreshed, so the just-created
  project can be picked up from the other tab instead of provisioning a second.
- Finish refuses a name that already belongs to another data table, which
  previously repointed it at the new database.
- Secrets go to the acting user's namespace instead of a literal `u/admin/`.
- The progress list no longer ticks "Created on Supabase" before the request is
  sent, and does not claim the database is ready when its check failed.
- The wizard's resume state is cleared when it closes, so reopening after an
  abandoned OAuth round trip is not stuck on step 2.
- The OAuth callback shares the session-storage key rather than repeating it.
- SupabaseConnect uses the shared provisioning helpers instead of a fork.
- Restores the doc comment displaced onto TestDataTableResourceQuery.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 22:17:31 +02:00
Guilhem Lemouel 800d74c9ac fix(frontend): do not claim the database is ready when its check failed
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 22:09:25 +02:00
Guilhem Lemouel c4d07876b8 chore: pin ee-repo-ref to the Supabase provisioning endpoints
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 22:07:22 +02:00
Guilhem Lemouel 0b632f80bb feat(frontend): guided setup wizard for data tables
On Cloud a data table cannot use the Windmill instance database, so a new
workspace hit a dead end: an alert telling the user to go find a PostgreSQL
resource somewhere else. Setting one up meant three disconnected places, and the
connection could only be tested after the config had already been saved.

Adds a three-step wizard (choose a database -> set it up -> name it) reached from
the data tables settings page:

- Supabase: signs in via the existing supabase_wizard OAuth client and creates
  the project from inside Windmill. Because db_pass is an input to project
  creation, Windmill sets the password and the user never visits a dashboard.
- Your own database: picks an existing postgresql resource, or adds one with a
  connection string through the form that already supports it.
- Windmill database: hands back to the inline row editor, since instance
  databases are provisioned by a superadmin.

Verifying access is no longer a step the user takes: Continue runs the check and
passing it is what advances the wizard, so a database that cannot create tables
never reaches the workspace config.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 22:04:29 +02:00
hugocasa c8ba771797 chore: track origin EE branch in worktree setup when absent locally (#9504)
* chore: track origin EE branch in worktree setup when absent locally

* chore: pass --track to worktree add so upstream is set regardless of git config

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

* chore: guard first worktree arm on local branch to avoid remote-only DWIM

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 10:13:16 +02:00
Ruben Fiszel c03bd34be9 chore(main): release 1.781.3 (#10563)
* chore(main): release 1.781.3

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.781.3
2026-08-06 10:02:27 +02:00
Ruben Fiszel 1846bd5ce5 fix(frontend): take the editor's post-edit content, not setCode's argument (#10562)
#10560 changed the script editor's change handler to read `e.detail` instead of
`editorCode`, on the reasoning that the two only diverge while the editor is
being torn down. They also diverge in a live editor: `setCode` dispatches the
string it was handed, but `alignCodeWithEditor` applies that string to Monaco
first, and the resulting `onDidChangeModelContent` runs `updateCode`
re-entrantly — so if the model normalized the text (EOL is the reachable case;
`ScriptBuilder` builds template content with a `\r\n` join), `editorCode`
already holds the buffer's version and the payload is the pre-normalization
one. Taking the payload leaves `code` disagreeing with what the editor shows,
which the external-write effect then tries to reconcile on every change.

The language-switch fix in that PR is `alignCodeWithEditor` clearing the timer
its own write armed; that part stands and is unaffected. This restores the
handler to the buffer-true read.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 09:59:19 +02:00
Ruben Fiszel 74737d16dd chore(main): release 1.781.2 (#10561)
* chore(main): release 1.781.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.781.2
2026-08-06 09:53:18 +02:00
Ruben Fiszel 56ea133366 fix(frontend): reset the editor content when the script language changes (#10560)
Picking a new language seeds the new template into `script.content`, which
`ScriptEditor` writes straight into Monaco. That write goes through
`onDidChangeModelContent`, which arms the keystroke debounce as if the user had
typed — so when the `{#key effectiveLang}` block then tears the editor down, the
unmount flush sees a pending timer, reads a stale `code`, and dispatches a change
that puts the previous language's template back. The editor kept showing the old
content under the new language.

`alignCodeWithEditor` now clears the timer its own write armed, restoring the
premise the unmount flush is guarded on: a pending timer means Monaco holds
something newer than `code`.

`ScriptEditor` also takes the change payload instead of re-reading `editorCode`.
A destroyed component's `bind:` writes no longer reach the parent, so the
re-read returned the value from before the change — which is what actually
wrote the old template back, and would equally have made the unmount flush
save stale text after real typing.

Fixes WIN-2330

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 09:49:20 +02:00
Ruben Fiszel c7ea530e1f chore(main): release 1.781.1 (#10556)
* chore(main): release 1.781.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.781.1
2026-08-06 02:17:57 +00:00
Ruben Fiszel 4c4387d52a feat(flow-editor): show an agent's tool-call status without moving the graph (#10557)
* feat(flow-editor): surface an agent's tool-call status without moving the graph

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

* fix: count only an agent's tool calls and key them in one place

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

* feat: report an agent's replies alongside its tool calls

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

* feat: break the agent summary down by action kind

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

* fix: key agent tool nodes by kind and keep the summary clear of the tool row

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

* refactor: read agent action status from the run's success array

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

* test: pin the tool joins a local run cannot reach

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

* fix: place the agent summary beside the step and match MCP paths bare

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

* fix: feed a single-step agent test's calls into the graph status

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 02:17:34 +00:00
Ruben Fiszel 386c66bef0 fix: open the expression property column on demand, not from focus (#10558) 2026-08-06 01:39:43 +00:00
Ruben Fiszel e4e7782517 fix: restore the flow expression editor's property side panel (#10555)
* fix: give flow expression editors their property side panel back

* fix: keep the picker column tied to an input that can receive the pick
2026-08-06 03:23:10 +02:00
Ruben Fiszel b2d38e0391 perf: keep run status out of flow graph node and edge data (#10554)
* perf: keep run status out of flow graph node and edge data

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

* fix: key the ai tool node memo on the agent's actions

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

* docs: state the edge-data and memo-key constraints as invariants

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

* fix: restore selection clearing and pin the zoom bar's border colour

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

* fix: show an agent's tool calls as they arrive instead of at step end

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

* perf: stop editor runs from rebuilding on agent tool calls

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 03:04:25 +02:00
Ruben Fiszel 5497710d47 chore(ci): drop the now-live dbt quickstart docs-link exemption (#10553) 2026-08-06 00:26:46 +02:00
Ruben Fiszel 9cf307f3ad chore(main): release 1.781.0 (#10528)
* chore(main): release 1.781.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.781.0
2026-08-06 00:04:25 +02:00
Ruben Fiszel c59b60c729 fix: keep the same_worker pin when a suspend ends without approval (#10552)
* fix: keep the same_worker pin when a suspend ends without approval

A disapproved or timed-out approval gate hands the flow back through the
UpdateFlow channel with unrecoverable = true. That flag means "the previous
step's worker died", and it is read by six sites. Five of them happen to want
what it does here, but continue_on_same_worker and continue_with_runners do
not: the worker that ran the approval step is alive, so unpinning the error
handler and routing it by tag breaks the ./shared contract of a same_worker
flow and can land it on a worker group that cannot run it — the same defect
#10551 fixed for the three producers that hand back a live flow.

Replace the boolean with StepFailureKind so the suspend producer can say
"worker alive, but this failure is not the module's to handle" instead of
overstating a worker death. The failed module's error policy is deliberately
still bypassed: the failure is recorded against the step the gate was holding
back, which never ran, so its retry would re-open the gate and its
continue_on_error would skip it outright (verified: the gated step is marked
Failure with a nil job id and the flow jumps past it). suspend.
continue_on_disapprove_timeout remains the way to continue past a gate.

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

* feat(flow-editor): flag that continue on error does not cover the approval gate

A resolved approval is recorded against the step the gate holds back, not
the step carrying the suspend, so continue_on_error never sees it: the flow
still stops on a disapproval or timeout. Point users at
suspend.continue_on_disapprove_timeout, which is what actually continues past
a gate, whenever both settings are on and that one is not.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 23:59:42 +02:00
AlexRV12 f11e8835fd fix: point re-opened previews at the tab already showing them (#10538)
* fix: point re-opened previews at the tab already showing them

* fix: judge composed preview mutations as one change

* fix: treat a fullscreen preview as displayed when deciding to flash
2026-08-05 20:18:11 +00:00
Guilhem 2c189fea14 fix(frontend): draw the tab strip's scroll bar instead of the native one (#10547)
* fix(frontend): draw the tab strip's scroll bar instead of the native one

The strip sizes its scroll row to the tabs, but a native horizontal
scrollbar claims layout height on top of that: Firefox spends 11px on
`scrollbar-width: thin` — `--wm-scrollbar-size` is WebKit-only, so the
4px it asks for is ignored there — which clipped the tabs at the top of
the 32px sessions strip and left a wide gutter under them.

Hide the native bar and draw a 4px thumb from `scrollLeft`/`scrollWidth`
instead: it costs no layout height, is the same size in every engine, and
sits on the strip's bottom edge, flush under the tabs. Tabs drop to `h-6`
so they clear it, and the strip's default height matches the sessions
caller's `h-8` so every strip has the same geometry.

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

* fix(frontend): clamp the tab strip thumb at both ends of its track

WebKit's elastic overscroll drives `scrollLeft` negative, which slid the
thumb out of the track's left edge and into the strip's padding.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 20:17:05 +00:00
Ruben Fiszel 154f8f461e feat(debugger): install debug session deps from the instance registry settings (#10550)
* feat(debugger): install debug session deps from the instance registry settings

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

* fix(debugger): keep install-time registry credentials out of the session-visible tree

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

* docs: drop em dashes from the debugger registry docs and comments

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

* fix(debugger): stop installing for a session that went away during the settings fetch

Also serves nativets sessions the npm settings their installer reads.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 20:16:38 +00:00
Ruben Fiszel 1aee22296e fix: keep the same_worker pin across a flow module that spawns no job (#10551)
When a module completes without spawning a job — an empty branch, an
empty for-loop, or a module already marked Success — the flow hands
itself back through the UpdateFlow channel, and the result processor
resumed it with unrecoverable = true regardless of what sent it. That
flag means "the previous step's worker died", which holds for none of
the three producers except a suspend that ended without approval.

The stale argument was inert until continue_on_same_worker and
continue_with_runners started reading it, since when the step after such
a module is pushed as an ordinary queued job. It is then routed by tag
and can land on any worker in the pool, breaking both the ./shared
directory contract and the guarantee that a same_worker flow stays on a
worker able to run it — a step whose tag resolves to a worker group that
cannot execute its language fails instantly, taking the flow with it.

Carry the flag on the UpdateFlow message so each producer states its own
case, rather than having the shared receiver assume the worst. The three
that hand back a live flow forward whatever their caller reported, so a
genuinely unrecoverable failure still crosses the hop unchanged.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 20:16:07 +00:00
Ruben Fiszel d9d6ec82ab feat: register mounted CA certificates in windmill_extra at startup (#10545)
* feat: register mounted CA certificates in windmill_extra at startup

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

* fix: only claim a CA update when update-ca-certificates can read the mount

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

* fix: detect mounted CA certificates the way update-ca-certificates finds them

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 19:13:29 +00:00
Ruben Fiszel a5423a81ca fix(debugger): confine prepare-deps under nsjail in both language paths (#10546)
`windmill prepare-deps` was spawned with Bun's raw `spawn` in both the Python and
the TypeScript session, bypassing the nsjail wrapping the debugged script itself
gets. With ENABLE_NSJAIL=true, `uv pip install` (source distributions run their
build backend) and `bun install` (postinstall scripts) therefore executed
package-supplied code unconfined, next to the LSP, multiplayer and gateway
services in the windmill-extra container.

Both installers now go through the same nsjail wrapper as the debuggee, which the
two files no longer build separately. The jail keeps the environment
(`keep_env`), which is what carries the registry credentials and CA settings into
the installer; the debugged script's environment is unchanged and still holds
neither.

Killing the installer also did not reap the `uv` or `bun` it had spawned: those
were reparented to init and kept downloading, so both the timeout and the
cancel-on-disconnect only half-worked. The installer now runs in its own process
group and is signalled as a group, reading the group id back from /proc rather
than assuming it, since a group kill aimed at the service's own group would take
down every service in the container.

Two things that cancellation exposed: a kill was reported to the client as an
install failure, since it ends the read with nothing to parse - blaming the user
for their own Stop; and the standalone Bun server's close handler only dropped
the session from its map, so nothing there was ever cleaned up. The teardown flag
is also scoped to a launch rather than the session, because cleanup() runs when a
program finishes normally too.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 19:08:04 +00:00
Ruben Fiszel e203ab087a feat: allow a dev workspace to have its own dev workspace (#10534)
* feat: allow a dev workspace to have its own dev workspace

Fixes WIN-2324

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

* fix: keep every dev workspace in a chain on a distinct deploy branch

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

* fix: count a dev workspace the caller has no seat in as holding its label

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

* refactor: keep the attach form standing when a candidate takes the last label

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

* fix: keep the label toggle visible when a candidate's dev workspace clashes

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

* docs: describe the cycle guard by what holds, not by what changed

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

* fix: refuse to archive a fork-backed dev workspace that owns a nested dev

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

* fix: put the deploy target and item filters under the pairing they configure

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

* fix: refuse to archive any dev workspace that owns a nested dev

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

* docs: fix the fixture family count

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

* fix: put the deploy target with the pairing line it restates, above protections

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

* fix: name the same family head in the workspace menu and the scope picker

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

* fix: stop offering to delete a dev workspace from the sidebar settings menu

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

* docs: state the visibility boundary the lineage root actually resolves to

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

* fix: serialize dev-pairing creation against teardown of the same workspace

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

* fix: lock both sides of an attach so adjacent pairings cannot share a label

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

* fix: serialize dev pairings on one key, the invariant being chain-wide

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

* fix: scope the pairing lock to the chains an operation reads

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

* fix: hold the pairing lock across renames and re-check the cycle under it

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

* fix: hide the fork-delete action until the workspace entry has loaded

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

* fix: lock archive before it reads the pairing state it acts on

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

* docs: describe the archive lock test by what it pins, and drop an unused fixture row

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 19:07:12 +00:00
Ruben Fiszel a3b79d7732 feat: add a load all to the tree view's per-folder pager (#10548)
* feat: add a load all next to load more in the tree view folder pager

* fix: bound tree node rendering and make a long load resumable

* fix: resume a failed first load from its saved cursor

* fix: size the show-more step by what a node holds, not what it renders

* fix: keep the pager visible mid-run and spin only the clicked button
2026-08-05 19:07:00 +00:00
Ruben Fiszel 0e42381df0 fix(triggers): stop one failing trigger count from zeroing the rest (#10549)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 19:05:26 +00:00
Ruben Fiszel 74c418570b fix: forward TLS trust roots to debug sessions and honor INIT_SCRIPT on windmill_extra (#10532)
* fix: forward proxy and TLS settings to debugger subprocesses

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

* fix: reach uv and the bun debugger with the forwarded network settings

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

* fix: map every CA variable spelling onto the one uv reads

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

* fix: keep package-index credentials out of debugged user code

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

* fix: install debugger dependencies outside the interpreter running user code

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

* fix: sandbox and bound the debugger dependency installer

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

* docs: correct the installer timeout rationale

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

* docs: scope the uv --cert note to the commands prepare-deps runs

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

* fix: build the debug venv against the interpreter that runs the script

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

* fix: do not start the debuggee for a session that already went away

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

* fix: remove the debug script when the session is gone before it starts

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 16:25:52 +00:00
Guilhem d9b10e7b0a fix(ai): collapse thinking to a status row with a thought-for duration (#10515)
* refactor(ai): render thinking blocks with the shared tool-call card

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

* feat(ai): collapse thinking to a status row with a thought-for duration

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

* refactor(ai): separate reasoning-timing reset from duration read

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

* fix(ai): render expanded thinking in the body font, not mono

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

* feat(ai): mark in-progress chat rows with a shimmer sweep

Thinking and tool calls both announced themselves with a spinner, which
carried no more information than the row already did and read as visual
noise once several tools ran in sequence.

A white copy of the label now sits over the coloured one and is revealed
through a travelling band, so a running row is marked by motion across
its own text rather than by a separate glyph. Both spinners and the brain
icon are gone, leaving the card with no icon slot at all, and every header
label settles on text-secondary.

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

* fix(ai): keep a running row marked under reduced motion

The shimmer is the only thing distinguishing a running tool row from a
settled one, and the reduced-motion rule removed it outright, so the two
became identical for those users. The band now degrades to a flat wash
instead of disappearing.

Also covers the reasoning-duration state machine: that thinking stops at
the first answer token rather than at the end of the turn, and that each
reasoning pass of a tool-using turn is timed from scratch.

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

* test(ai): restore the clock spy after the reasoning-duration tests

The file-level hook only clears call records, so the Date.now spy stayed
installed and would freeze time for anything appended after this block.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 15:53:48 +02:00
Ruben Fiszel 3beb0b9496 start python debug sessions whose script has third-party imports (#10537)
* fix(debugger): return PrepareResult when the service prepared the venv

`prepare_dependencies()` returns `PrepareResult` on every path except the
service-prepared short-circuit, which returned the venv path as a bare `str`.
`handle_launch` reads `prepared.error` on it, so every Python session whose
script has a third-party import raised `AttributeError`, hung, and failed at
180s with `Debugpy command timeout: launch`.

The two consumers of a prepare-deps response also read a `stderr` key the CLI
does not emit; the field is `install_stderr`, and it carries the same text
`error` already wraps in a sentence, so take one rather than joining both.

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

* fix(debugger): keep the failing step in the launch message, drop the dead branch

Preferring the raw `install_stderr` made `_first_line` pick uv's opening
progress line, so a refused launch reported "Using Python 3.12.13 environment
at: venv" — which reads like success. `error` is the same text prefixed with the
step that failed, so it is the better of the two to condense.

The installer-diagnostics pass over a `success: true` response is unreachable:
every `success: true` site in prepare_deps.rs sets `install_stderr: None`, and
its comment claimed the opposite of what that file documents. It existed to work
around a producer that warned and returned success on a failed `uv pip install`;
that producer now returns `success: false`.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 15:51:43 +02:00
Guilhem 616d4fe167 fix: edit-in-dev-workspace dead-ends, wraps, and misses the tree view (#10354)
* fix: stop the homepage edit-in-fork button from wrapping

* fix: show the full edit-in-fork label anywhere on the button

* fix: thread showEditButton through the homepage tree view

* fix: match the edit-in-fork button styling to the normal edit button

* fix: edit in dev workspace dead-ends on items the dev workspace lacks

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

* fix: pull the item's folder before copying it into the dev workspace

* fix: speak the compare page's update vocabulary in the dev-workspace prompt

* fix: raw app with no stylesheet was undeployable across workspaces

* fix: drop the raw-app stylesheet workaround now that the backend serves one

The frontend wrapped `getRawAppData` to report a missing `.css` as empty,
because a raw app with no stylesheet stores no css blob and the shared deploy
treats the resulting 404 as fatal. #10364 fixed that at the source: the backend
now serves an empty body for a missing stylesheet, so the wrapper guards a 404
that no longer happens.

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

* fix: drop the fork icon from the edit-in-dev-workspace affordances

The row button carried both a pen and a fork, and the menu entries and detail
page buttons carried a fork alone — where the menus already used that same icon
for Duplicate/Fork, so the two entries were indistinguishable. The action is an
edit, so it takes the pen everywhere, matching the ordinary Edit button.

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

* fix: send edit in dev workspace to the item's editor

The affordance landed on the item's page in the dev workspace and left the user
to open the editor from there. It says "Edit", so it goes to the editor:
`/scripts/edit/...?workspace=<dev>` and the equivalent for flows and both app
kinds. `?workspace=` still does the workspace switch, which the logged layout
applies on any route.

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

* feat: choose the on-behalf-of user when updating the dev workspace

The prompt deployed the item with no say over the identity it would run under,
so an item that ran on behalf of someone in prod silently became the deploying
user's in the dev workspace. It now offers the same choice the compare page
does, under the same rules: shown only when the source item carries an
on_behalf_of, picking anyone but yourself gated on admin/wm_deployers in the
target, and confirming blocked until a choice is made — including while the
lookup that decides whether one is needed is still in flight.

The prompt also stops offering the compare page inline; the confirm button
still leads there when the user can't deploy into the dev workspace.

Two fixes the reused selector needed to work inside a dialog:
- ConfirmationModal takes `confirmDisabled`, which also blocks the Enter binding.
- The popover's z-index is now overridable, and the user picker is portalled.
  A ConfirmationModal renders above the popover layer, and its card is
  transformed for the open transition, which makes it the containing block for
  the picker's `fixed` positioning — so both opened behind, and the picker was
  laid out inside the card instead of the viewport.

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

* fix: check deploy rights per item before prompting to update the dev workspace

* fix: read the compare page link before closing the dev-workspace prompt

The link is derived from the request the prompt is answering, so closing first
left an empty string to navigate to: refusing users saw the dialog dismiss and
stay put, with no way through to the compare page.

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

* fix: keep the new-tab promise and speak up when a popup is blocked

Three defects found by successive cold reviews of the click-time resolution
added earlier in this branch, each one only reachable once the previous fix
existed:

- Safari refuses `window.open` from any promise continuation however fast it
  resolves, so the tab the editor dropdown opens after its existence probe
  never appeared there. `claimTab` takes the tab inside the click's own
  transient activation and points it at the answer once it lands, releasing it
  when there is nothing to show.
- That left the two halves of the same action disagreeing: the entry promises
  never to navigate the editor away, but when the item turned out to be missing
  the prompt took over and navigated in place. The request now carries
  `openInNewTab`, and every destination the prompt can reach honours it.
- With popups blocked the fallback called `window.open` without checking, so a
  successful deploy closed the prompt and did nothing, silently. It now names
  what it could not open.

`openEditInFork` also takes the workspace explicitly. The four editor dropdowns
computed their label from `opWorkspace` but resolved the action from the
navigation store, and `prodWorkspaceId` feeds `deployItem({ workspaceFrom })` —
so a session pane would have deployed from the wrong workspace.

`checkPathWritePermission` is exported with an injectable folder probe and
covered by table-driven cases, chiefly to pin its two fail-open branches, which
otherwise read as dead code inviting deletion.

The two unrelated whitespace hunks in ScriptBuilder.svelte are the repo's
format-on-save hook fixing pre-existing violations in a file this touched.

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

* fix: create the dev workspace's missing folder without overwriting it

`ensureFolder` delegated to the shared `deployItem`, which re-probes and
switches to `updateFolder` when the folder turns out to exist. Nobody asked for
that folder to be deployed — it is created only so the item has somewhere to
land — so a folder created between the two probes had its owners, ACL, summary
and labels silently replaced with the source workspace's. Creating is now
create-only, and losing that race counts as success: the folder exists, which is
all the caller needed.

The same delegation dropped `default_permissioned_as` and `labels`, which the
shared folder deploy does not send. A folder copied without its create-time
identity rules applies none, so an item landing inside it with no on_behalf_of
of its own resolves to whoever deployed it rather than to the principal the
source folder would have chosen — the exact substitution the rest of this branch
exists to prevent. Both are now carried across.

Also check `window.open` in the no-dev-workspace branch of `openEditInFork`. The
branch beneath it already reported a blocked popup; this one returned as if it
had opened something.

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

* fix: translate copied folder identity rules into the target workspace

A `u/<username>` names a workspace-local account, so copying a folder's
`default_permissioned_as` verbatim was wrong in two directions: the same
username in the dev workspace can be a different person, who would then be
granted the item; and a username with no account there at all passes the
folder-create check, which is structural, only to fail every subsequent item
deploy on the existence check, including the retry — the folder now exists, so
`ensureFolder` short-circuits and the deploy fails identically, with no way out
of the prompt.

Rules are now resolved source username -> email -> target username, since email
is the only identifier stable across workspaces, and a rule whose principal has
no account in the target is dropped rather than carried. Dropping one makes the
copied folder less restrictive than its source, which is not something to
discover later from an item running as the wrong user, so it is reported.

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

* fix: refuse to overwrite a concurrent item, and translate every folder principal

Four findings from CI review, all on the implicit half of this flow — the writes
the user did not explicitly ask for.

The item write is now create-only. The shared `deployItem` re-probes and silently
switches to an update, so the caller that acts on an item being *absent* could
still overwrite whoever landed it between the two probes. Rather than
reimplementing the per-kind deploys, the frontend's own provider refuses exactly
the three writes that branch reaches for — `updateFlow`, `updateApp`/
`updateAppRaw`, and a `createScript` carrying a `parent_hash`, which is what
makes an otherwise identical create an update. A refusal reports `conflict`, and
the prompt opens their version instead of replacing it.

Folder principals are translated rather than copied. `u/<username>` is
workspace-local, so a verbatim copy either names nobody or names a different
account that happens to share the username. Users now resolve source username ->
email -> target username, and the two kinds of unresolvable principal are
separated because they fail differently: an owner or ACL entry is dropped, which
can only narrow the folder and leaves the creator owning it; an identity rule
refuses the copy outright, because dropping it runs the item as the deployer and
keeping it creates a folder the server then rejects every deploy into.

Groups resolve against `listGroups` rather than `listGroupNames`, which unions in
instance groups that folder rules do not resolve against — a same-named instance
group would otherwise let an unusable rule through.

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

* fix: read every page of workspace groups before judging a folder principal

`listGroups` paginates, and the `perPage: 100` it was called with is narrower
than the server's own default of 1000 — so a group past the first page read as
having no account in the target. Since an unresolvable identity rule now refuses
the whole folder copy, that turned into a refusal naming a group that does
exist, and an owner or ACL entry on a later page was dropped silently. Read
until a page comes back short, with a size check as the backstop for a server
that ignores `page`.

`list_users` is unpaginated, so the user half of the same lookup was never
affected.

Also move `makeProvider`'s doc block back onto `makeProvider`; adding
`DeployConflict` had left it documenting the type instead.

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

* docs: reattach principalTranslator's doc block to principalTranslator

Adding `workspaceGroupNames` above it left the block documenting the helper,
the same way adding `DeployConflict` had displaced `makeProvider`'s.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 14:21:14 +02:00
Ruben Fiszel 4fe4fac358 feat(mcp): serve the 2026-07-28 spec alongside the legacy protocol (#10535)
* feat(mcp): serve the 2026-07-28 spec alongside the legacy protocol

* fix(mcp): keep oauth discovery strict and preserve request limits

* fix(mcp): allow the protocol's own headers through CORS

* fix(mcp): expose the auth challenge to browser clients

* chore: update ee-repo-ref to c1665a881b61616f96ffe7702b44840905304660

This commit updates the EE repository reference after PR #711 was merged in windmill-ee-private.

Previous ee-repo-ref: bc1c001e3e386342415dfb8ac31c6b97f6629320

New ee-repo-ref: c1665a881b61616f96ffe7702b44840905304660

Automated by sync-ee-ref workflow.

---------

Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-08-05 13:52:09 +02:00
Ruben Fiszel 9f3f4fb6d0 stop swallowing Ctrl/Cmd+Shift+S in the editors (#10530)
* fix(frontend): stop swallowing Ctrl/Cmd+Shift+S in the editors

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

* fix(frontend): make Ctrl/Cmd+S from a focused Monaco flush the draft

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

* fix(frontend): broadcast the Monaco save shortcut after the effect flush

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 13:49:35 +02:00
Ruben Fiszel 29e179f787 fix(debugger): report python debugger dependency install failures instead of timing out (#10531)
* fix: report python debugger dependency install failures instead of timing out

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

* fix: surface swallowed installer errors and stream debugger prepare progress

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

* fix: reap the python debugger on a failed launch and bound prepare-deps

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

* fix: match uv failure output by stripping progress instead of matching errors

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

* fix: treat uv build, download and warning lines as install progress

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 13:46:36 +02:00
Ruben Fiszel 7d153d5750 fix(debugger): pass python index settings to prepare-deps and report failures (#10533)
* fix: honor python index settings in prepare-deps and report install failures

* fix: forward python registry env to the debugger's prepare-deps

* fix: scope registry credentials to the prepare-deps subprocess

* fix: install python debug dependencies from the service, not the session

* fix: bound the debugger dependency install and keep the proxy bypass default

* docs: name the nsjail config that isolates debug sessions
2026-08-05 13:43:12 +02:00
Guilhem 09c8f3b1f3 feat: redesign flow step, loop and branch settings panels (#10026)
* feat: responsive modal step panel for the flow editor in sessions

On narrow layouts the flow editor's step-details pane opens as a modal
(double-click a graph node) instead of a split pane, with a dock/float
toggle. Scoped to sessions via allowModalPanel; the full-page editor is
unchanged.

- FlowEditor: modal/docked modes gated by mount width + allowModalPanel,
  small header (step-id Badge + subtle dock/close), standing
  double-click hint, and a per-step hint in the name tooltip
- selectionManager: onSelectIntent hook so flow-level panels (settings,
  input, triggers…) open the modal on single click
- PropPickerWrapper: collapse the prop picker until connect and animate
  it in via AnimatedPane (runs-page pattern), no blue connect ring in
  modal mode
- StepInputGen: drop the TAB/Wand autocompletion button + spinner
  (feature still works via focus + Tab)
- InputTransformForm: decouple the Help dropdown from the AI suggestion
- FlowModuleHeader: move 'Save to workspace' into an ellipsis dropdown

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

* fix: loop editor rendering and nested splitpanes splitters in the sessions modal

- Loop iterator/parallelism: keep the picker split pane (forceExpanded) so
  the editor fills its box and the picker shows; the collapse-until-connect
  mode stays for the step inputs
- Remove the intrusive AI TAB/Wand autocompletion button from IteratorGen
  (generation still runs headless via focus + Tab)
- Size the iterator connect plug and restyle the loop header/labels/toggles
- Scope the global `.splitter-hidden` splitter-hiding rule to direct children
  so it no longer leaks into nested Splitpanes under the sessions preview

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

* feat: redesign flow step advanced settings as a single toggle-first column

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

* feat: taller step test pane by default and restyle advanced section titles

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

* feat: show flow run-settings params disabled when a setting is toggled off

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

* feat: single-column for-loop panel reusing the run-settings accordion

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

* feat: single-column while-loop panel reusing the run-settings accordion

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

* feat: single-column branch panels reusing the run-settings accordion

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

* fix: auto-open modal panel when creating an AI agent tool

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

* feat: redesign branch panels with card layout and shared predicate editor

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

* refactor: remove per-setting status badges from flow map nodes

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

* chore: sync package-lock after windmill-utils-internal bump

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

* style: polish prop-picker plug button and branch panel layouts

* fix: persist skip-if-stopped toggles in early stop settings

* fix: open the step panel modal on demand and cap its width

* fix: restore graph step setting badges, strip panel header chips instead

* feat: docked panel header with detach action and open-details step menu

* feat: width-based panel mode on every surface with inline detach action

* refactor: single source for flow step settings and their defaults

* docs: pin flow editor vocabulary in CONTEXT.md

* fix: open the trigger panel on double click or a specific trigger

* fix: keep module pickers inside their pane and dismissable

* fix: drop the misleading chevron on the MCP tool entry

* fix: resolve flow approvals against the job's workspace, not the nav one

* refactor: derive the approval workspace from the job, not from callers

* fix: restore S3 snippets and gate params while their setting is off

* fix: restore branch mock controls and address review findings

* chore: drop stray debug log from the flow map item

* feat: pinned output section for loop and branch panels

* fix: open the panel for deliberate navigation from the flow header

* perf: mount branch predicate editors on demand

* fix: skip predicate picker previews the previous step's result

* fix: flow-level graph nodes open their panel on a single click

* fix: open the step panel for AI chat selections, not for undo

* chore: drop dead console.log and duplicated modalPanel doc

* fix: re-sync expression editors and scope error-handler settings

* fix: match the failure module exactly and ignore unselectable nodes

* fix: keep concurrency editable, honour module cache_ttl, tighten panel ids

* fix: open panel from indirect selections, use presence for value-driven toggles

* fix: don't open settings on error-handler delete, flush editors on unmount

* fix: guard editor destroy flush, keep retry kind reachable

* refactor: name the run settings panel after the domain vocabulary

* fix: only write editor flushes to the step they belong to

* fix: bind step panels by id so a delete can't retarget editor writes

* fix: don't let the trigger picker's escape close the drawer beneath it

* docs: condense two comments to the constraint they record

* fix: arbitrate escape through the overlay stack instead of deferring to it

* fix: key nested step blocks by identity so anchored bindings can't go stale

* fix: untrack the overlay-stack push and drop the frozen branch binding

* chore: state the escape rationale once, key branch lists, format

* fix: let the topmost overlay own escape instead of the graph

* fix: keep the dynamic-input help box out of static template fields

* fix: restore the graph connect on the for-loop iterator

* fix: end connect mode with the modal and keep it to docked panels

* fix: never enter graph connect mode from the modal panel

* fix: reveal inserted steps, restore editor pane size, unleak the drawer stack

* fix: keep the enable-AI popover reachable in session panes

* feat: add the connect policy and its single armed slot

* refactor: one picker for every expression input

* refactor: route every connect through one armed slot

* fix: give every connect button the same footprint

* fix: keep the connect ring from showing through the button

* fix: keep flow card actions right-aligned beside the detach button

* fix: give the connect ring an opaque ground to mask against

* feat: dock the panel back without reopening it

* feat: dock the panel from the graph control bar

* style: round the graph control bar and size its glyphs

* style: customize the graph controls through their supported api

* style: build the graph control bar from lucide icons

* fix: use the graph's tooltip component in the zoom controls

* style: pad the graph controls and enlarge their glyphs

* style: pad the graph controls and put dock at the bar's end

* refactor: give settings rows the same popover picker as other expressions

* fix: pass the wrapper's pickable properties to nested inputs

* refactor: stack step settings and render every expression through the step input form

* feat: split loop panels into tabs and rework the approval form

* feat: anchor drawers to their host pane and give them a size floor

* fix: mark the loop iterator expression as required

* refactor: badge ee-only toggles instead of a warning line

* fix: flag an empty loop iterator expression as an error

* refactor: pick the early-stop flow status from one toggle group

* fix: keep parallel loops uncapped unless a limit is opted into

* fix: scope the overlay stack to its host and disarm connect on dismissal

* fix: anchor the trigger picker to its host pane

* feat: move diff into the menu when the top bar is narrow

* fix: gate the result logs toggle to the graph popover

* feat: raise the modal-panel breakpoint to 1280

* fix: anchor flow editor popovers and fullscreen to their host pane

* fix: anchor overlays to their host pane and mute them when hidden

* fix: portal hosted modals and menus into the pane they anchor to

* fix: keep non-listening dialogs off the overlay stack

* fix: drop the topmost gate from confirmation dialogs

* fix: silence overlays in a collapsed preview panel

* feat: rework the branch panels with tabs, reordering and add/delete

* refactor: fold the detached-panel chrome into the card header

* fix: give every flow panel a titled card header

* fix: stop the step panel oscillating on an auto-height editor

* feat: consolidate script panel actions and restore branch predicate AI

* fix: restore the logs toggle on the flow result popover

* fix: collapse the idle property picker in modal step panels

* fix: stop the docked pane scrolling alongside its panel

* fix: space the last settings row off the panel bottom

* revert: always show the property picker pane in step panels

* chore: keep the inline script AI button identical to main

* fix: ask for AI input suggestions on click, not on hover

* fix: keep graph connects armed and remount the parallelism input

* style: reveal the predicate AI button on row hover

* style: give branch cards a handle and delete column

* refactor: arbitrate flow overlay escape through Disposable

* fix: give the popover picker its results and re-narrow the EE badge

* docs: correct loopSubset and guard the modal width measurement

* fix: insert picked properties at the cursor in expression inputs

* fix: give the expanded-subflow panel the shared header chrome

* style: rename the suspend setting to Suspend until approval/resume

* feat: open a step's modal when clicking the step already selected

* feat: add an auto/attached/detached toggle for the step panel

* refactor: pick the step panel's placement from one named menu

* refactor: keep the panel-mode module's exports to what is consumed

* feat: show each configured setting's value on its badge

* fix: carry the suspend rename into the step settings registry

* docs: name both gestures in the step explore hint

* test: pin where the step panel goes for a given width and preference

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 12:25:25 +02:00
Ruben Fiszel 1dcb6bb900 fix(frontend): filter the AI Sandbox entry by the flow insert search (#10529)
Also drops the now-stale (new) badge on the Claude Code picker entry.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 10:07:22 +02:00
Guilhem aa91619bb6 fix: flow step picker layout and single hover/keyboard highlight (#10488)
* fix: keep flow step picker rows on one line and highlight only one

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

* fix: restore hover on standalone picker rows and drop phantom ai slots

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

* fix: drop inert picker resize and align hub rows with workspace rows

The step picker popover carried `!resize` but computes `overflow: visible`, so CSS
`resize` never applied and the handle did nothing. Dropping it also pins the inner
height at 464px, keeping `displayPath` off everywhere except the content-sized
trigger picker.

Hub rows there rendered summary and path inside a fixed 28px button; give them the
same `h-auto min-h-7 py-1` the workspace rows got. Guard `hover:bg-transparent` on
`onHover` in both pickers so all three agree, and drop the unconditional `title` on
TopLevelNode, which put a native tooltip on every kind button.

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

* fix: keep GenAiQuick's CSS hover when it is not wired into the shared index

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 09:28:13 +02:00
Ruben Fiszel 055a9c2690 chore(main): release 1.780.0 (#10527)
* chore(main): release 1.780.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.780.0
2026-08-05 00:37:06 +02:00
Ruben Fiszel 340d3cd565 feat(dbt): reach any dbt adapter through a dbt_profile resource, and constrain the warehouse picker (#10525)
* feat(dbt): reach any dbt adapter through a dbt_profile resource, and constrain the warehouse picker

The workspace dbt warehouse picker listed every resource in the workspace, so a
slack or github resource was an offerable answer to a field that can only be a
warehouse. Constraining it exposed that the set of resource types that actually
work is both smaller than the docs claim and too small to be useful:

- `render_profile` translates only six adapters from a Windmill resource; the
  rest (clickhouse, duckdb, salesforce, mssql, oracle) refused one outright.
- `redshift` and `duckdb` name no resource type anywhere, so two of the
  adapters the quickstart advertises were unreachable.
- the `databricks` resource carries `workspace_url`, while the renderer demanded
  `host`, so that warehouse could never render at all.

So the picker gets a constraint and dbt gets an escape hatch wide enough to make
it honest. `dbt_profile` is a resource whose value IS a `profiles.yml` target —
`{ type, target }` — passed to dbt unchanged, so any adapter and any key it
documents works.

`DbtAdapter` is now open: it carries dbt's own `type:` spelling plus an optional
`KnownAdapter` (the eleven Windmill has facts about — a field mapping, a pip
package, the license gate). Anything else is carried by name and installed as
`dbt-<name>`, the convention every adapter on PyPI follows, so "whatever dbt
supports" no longer means "whatever this enum lists". The license gate is
unaffected: `sqlserver`/`oracle` still resolve to their `KnownAdapter` and are
still gated. The name is confined to `[a-z0-9_-]` starting alphanumeric because
it reaches a pip requirement and a venv path on the host.

Two adjacent fixes fall out: the project's own `profiles.yml` and the
descriptor's `profile.type` now accept any adapter instead of the closed list,
and a databricks resource renders its `host` from `workspace_url`.

The picker is constrained to `dbt_profile` plus the translated types, so nothing
it offers can fail for want of a mapping.

Fixes WIN-2320

* fix: drop the unused DbtAdapter::from_resource_type wrapper

Nothing calls it: a Windmill resource type maps through
KnownAdapter::from_resource_type, and the executor resolves an adapter from
the resource's own dbt spelling or by inference. CI builds with -D warnings,
so the dead wrapper failed every backend check.

* fix(dbt): make dbt_profile the block itself, and address the review findings

**A `dbt_profile`'s value IS a `profiles.yml` output block**, `type` included.
It was `{ type, output }`, which asked the user to restructure their block
before pasting it — a translation step, in the one type that exists to avoid
translation. The schema now declares no properties, so the resource form renders
a single JSON editor over the value.

That means the value's shape can no longer say what it is: a `dbt_profile` and
Windmill's bigquery resource are both objects with a `type` (the latter says
`type: service_account`). So the warehouse carries its resource's type
(`DbtWarehouseConnection.resource_type`), and detection is exact. It also makes
decision 9's "the resource type name is the authority" true at runtime for the
translated path, which until now resolved its adapter by sniffing fields.

Review findings, all three reviewers:

- **[P0] an author-chosen adapter became an unsandboxed PyPI install.** `dbt-` is
  not a reserved prefix, and `provision_core_1x` installs through `run_tool`,
  outside the nsjail ordinary dependency installation uses — so `dbt-<name>` from
  a script author's `type` could run a PEP 517 build backend as the worker. Now
  gated on a list of published adapters plus `DBT_EXTRA_ADAPTERS`, so trust stays
  the admin's call. The open set survives: the engines that ship their adapters
  install nothing and take any type.
- **[P1] `type: fabric` rendered as `sqlserver`.** dbt's `type:` was resolved
  through the resource-type table, where `fabric` is a Windmill alias for SQL
  Server — so a Fabric profile installed dbt-sqlserver, was enterprise-gated, and
  failed on an ODBC driver without ever naming Fabric. dbt types now have their
  own table.
- **[P1] two spellings of one adapter compared unequal.** `PartialEq` covers the
  carried name, so `postgres` != `postgresql` even resolving to one adapter, and
  the descriptor/resource check rejected valid configs with a message naming the
  same adapter twice. The name is normalised to the adapter's dbt spelling.
- **[P2] identity keys.** `database_key` is what a Windmill resource spells it,
  and only translated adapters have one; the rest read dbt's `database`.
- **[P2] duplicate `sslrootcert`** when a block carried both a PEM and a path.

Verified with three real dbt builds: a flat `dbt_profile` postgres block, the
same with `type: postgresql` under a `profile.type: postgres` descriptor (the
alias case, which failed before), and trino for the unknown-adapter path.

* docs(dbt): say that installing an adapter is gated, not just using one

The open-adapter text promised every future adapter is installed as dbt-<name>,
which ensure_adapter_installable refuses outside PUBLISHED_ADAPTERS and
DBT_EXTRA_ADAPTERS. Separates the two: rendering, licensing and identity are open
to any adapter, and only the dbt-core 1.x PyPI install is gated, because that is
the step that runs outside the sandbox.

* fix(dbt): keep a dbt_profile's own sslrootcert when Windmill writes none

The previous round skipped the block's sslrootcert unconditionally to avoid
emitting the key twice, which drops a path-only CA reference — a certificate
baked into the image or mounted on the worker, which is the block's own trust
source. Skipped now only when a root_certificate_pem is present, which is when
Windmill writes a replacement.

* fix(frontend): let a resource type declare no properties

A schema without `properties` is a JSON-edited resource type, not a broken one -
`dbt_profile` is a profiles.yml block whose keys belong to its adapter, so there
is nothing for Windmill to declare. Both editors assumed properties exist:

- ResourceEditor threw on Object.keys(undefined) while deriving the field order,
  which left the drawer on its loading skeleton forever, so the resource could
  not be viewed or edited at all.
- ApiConnectForm caught the same throw and reported the type as missing from the
  workspace, offering to sync a type it already had.

Both now fall back to the raw JSON editor, which is what usesRawEditor already
intended for a schema with no properties.

* chore: cut the new comments to AGENTS.md's four-line cap

Each still states its constraint once; the long-form rationale belongs in
docs/dbt-runtime.md and the PR, not beside the code.

* fix(dbt): keep a dbt_profile's empty and nested collections intact

A block with no children reads back as null, so `extensions: []` reached the
adapter as a missing value rather than the empty list dbt was handed, and a
nested array went through the scalar path and arrived as a quoted JSON string.
Both are keys dbt passes to the adapter as it finds them, so the type has to
survive: empty collections are emitted inline, and the value half of an entry
recurses instead of bottoming out at a scalar.

The test parses the rendered YAML back rather than string-matching it, since
what matters is what a YAML reader sees.

Also cuts DbtWarehouseConnection.resource_type's comment to the four-line cap.
2026-08-05 00:34:26 +02:00
Ruben Fiszel 552ad9c859 fix: reflect custom tag add/remove in the manage-tags drawer immediately (#10526)
* fix: reflect custom tag add/remove in the manage-tags drawer immediately

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

* fix: do not fail the custom_tags write when the cache refresh errors

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 23:57:35 +02:00
Ruben Fiszel 59072a1273 chore(main): release 1.779.0 (#10496)
* chore(main): release 1.779.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.779.0
2026-08-04 20:45:57 +02:00
Ruben Fiszel 4254686758 feat: show far more in the home tree view and say what is not loaded (#10519)
* feat: show far more in the home tree view and say what is not loaded

* feat: let every folder in the home tree page within its own prefix

* fix: count leaves in nested badges and stop transient subtree mounts

* fix: merge nested pages instead of replacing rows an ancestor loaded

* docs: tighten the tree prefix-loading invariants
2026-08-04 20:41:30 +02:00
Ruben Fiszel 0d1cb818ee feat: preview and edit steps inside expanded subflows (#10520)
* feat: preview and edit expanded subflow steps in the flow editor

* fix: hide subflow edit button when no flow editor drawer is available

* fix: address review findings on expanded subflow step panel

* fix: base-prefix subflow links and bound the expanded subflow module cache

* fix: do not let a pre-deploy response repopulate the invalidated subflow cache

* fix: guard expanded subflow reloads against collapse and encode workspace in link

* fix: commit an expanded subflow reload only onto the expansion it fetched for
2026-08-04 20:40:35 +02:00
Guilhem 62b2f4b067 fix: bundle a vector emoji font so emoji scale with flow graph zoom (#10498)
* fix: bundle a vector emoji font so emoji scale with flow graph zoom

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

* fix: include the upstream copyright notice in the bundled OFL license

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

* fix: match the bundled license notice to the shipped font binaries

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

* docs: correct the unicode-range gating comment

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 18:34:38 +00:00