Commit Graph
14745 Commits
Author SHA1 Message Date
Diego Imbert 01567c2d30 fix(datatables): a resource-backed clone is not a copy, and the two helpers state their contract 2026-09-06 01:28:56 +02:00
Diego Imbert 73628dea4b fix(datatables): an archived fork still reaches the database, and raw-app AI tools run as the app's role 2026-09-06 00:56:55 +02:00
Diego Imbert 1aac111b80 fix(datatables): only the opt-in is gated, and a rename leaves no permissioned copy behind 2026-09-06 00:02:05 +02:00
Diego Imbert ddae0ec609 fix(datatables): permissions are enabled where the workspace alone reaches the database 2026-09-05 21:26:17 +02:00
Diego Imbert d4c0c83e0b Merge remote-tracking branch 'origin/main' into datatable-perms-3 2026-09-05 21:07:24 +02:00
Diego ImbertandClaude Opus 5 af0477c16f fix(datatables): a workspace-id change leaves one owner for each login
The rename copies the whole config, so the archived id and the live one both
named the same generated logins — and deleting the archive planned drops for
logins the renamed workspace was still using, destroying their grants.

The archived copy stops naming them: it is kept for reference, and a
reference needs no credentials. The renamed workspace keeps working, since
resolution uses the stored `pg_rolename` rather than the generated one, and
its next permissions save finds the stored name no longer matches what this
workspace id generates and renames the login to match — under the ownership
proof that rename already carries.

Also: a pending row action now has to name the same data table the manager
is open on, not merely fail to differ from it. `undefined` — a plain
postgres resource, or a DuckLake — is somewhere else too, and a matching
schema and table name there would have run it. Both drawers drop what is
pending when they close.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-05 21:06:36 +02:00
Diego ImbertandClaude Opus 5 6eb63fa58b fix(datatables): the roles drawer's Save button is sized like every other
It was the one `unifiedSize="xs"` (h-5) among the drawer action buttons,
where the convention is `md` (h-8), so it read as a different, lesser
control than the Save it sits beside everywhere else.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-05 21:06:35 +02:00
Diego ImbertandClaude Opus 5 63ef7663fc fix(datatables): permissions cannot be enabled from a fork
A fork's data table points either at the database of the workspace it was
forked from — where roles created here hold grants that workspace's own
config does not name, so its next save adopts or destroys them — or at a
clone whose whole database the fork can drop, taking the roles with it.
Neither is a place to build an access model, so the save and the preview
refuse it.

Turning them off stays allowed, or a fork carrying permissions from before
this rule could never be rid of them and the roles behind them never
dropped. The drawer says why, and the toggle is off only where it would be
refused.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-05 21:06:35 +02:00
Ruben Fiszelandrubenfiszel c37f59e22a chore(main): release 1.804.0 (#10963)
* chore(main): release 1.804.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.804.0
2026-09-05 11:11:54 +00:00
Alexander PetricandClaude Fable 5 a2417f6fb6 sign release images with cosign, embed SBOMs, attach SLSA provenance (#10983)
* feat: sign release images with cosign and attach SBOM + SLSA provenance

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8mi68bMNUFwCge7xAqyky

* fix: pin cosign-installer to exact version (no floating v4 tag exists)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8mi68bMNUFwCge7xAqyky

* fix: embed SBOMs at build time via depot instead of rekor-bound cosign attest

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8mi68bMNUFwCge7xAqyky

* docs: latest/main tags are only signed until the next main push

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8mi68bMNUFwCge7xAqyky

* fix: gate signing on push events in cli/extra workflows, verify version tag

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8mi68bMNUFwCge7xAqyky

* fix: refuse tag-targeted dispatches in publish workflows, use GITHUB_REF env

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8mi68bMNUFwCge7xAqyky

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 11:11:20 +00:00
9f7908e262 fix(oauth): show the account chooser on an explicit Google/Microsoft login (#10961)
* fix(oauth): show the account chooser on Google/Microsoft login

Without `prompt=select_account`, Google and Microsoft silently reuse the single
active browser session, so a user with more than one account has no way to pick
which one to sign in with.

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

* chore: pin ee ref for the oauth login extra_params fix

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

* fix(oauth): only ask for the account chooser on an explicit login click

The login page now sends `user_initiated=true` when someone clicks a
provider button, and the backend applies the provider's `extra_params`
only for those requests.

Someone whose browser holds a single Google session whose email is
already registered under a different login type hits
"an user with the email associated to this login exists but with a
different login type" and, with no account chooser, has no way to offer
a different account. The chooser belongs on that click.

It does not belong on the `auto_login_provider` redirect, whose whole
purpose is to sign a public-app or approval-page visitor in without
interaction.

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

* fix(oauth): make the account chooser the default, not the opt-in

The login page now flags only the `auto_login_provider` redirect, with
`auto=true`; every other login — a click on a provider button, or the
endpoint opened as a plain URL — gets the provider's extra params.

`/api/oauth/login/*` is whitelisted in `public_app_layer` and reachable
directly, so an opt-in flag would silently drop the account chooser for
every caller that is not our own button.

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

* chore: update ee-repo-ref to f5d6b6b8dd00b0141308337ac97f4685781f2b1c

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

Previous ee-repo-ref: 5684bb0f63dce08d6ce9ab0183072c8b4fce4b2e

New ee-repo-ref: f5d6b6b8dd00b0141308337ac97f4685781f2b1c

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-09-05 11:08:27 +00:00
Ruben FiszelandClaude Opus 5 d2019d7b5d only warn about manual action when the username actually changes (#10991)
* fix: only warn about manual action when the username actually changes

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

* fix: block the rename until the current usernames are known

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 11:08:12 +00:00
Diego ImbertandClaude Opus 5 a0295b20c4 fix(frontend): render ordered lists in markdown descriptions (#10973)
* fix(frontend): render ordered lists in markdown descriptions

`GfmMarkdown` defaulted to `prose-xs`, which Tailwind Typography does not
define — the class only ever matched four hand-rolled rules in app.css, all
scoped to `ul`. Every surface on that default (script and flow descriptions,
flow-graph notes, markdown job results) therefore rendered `<ol>` with
Preflight's `list-style: none` and no typography at all: no numbers, no
heading or paragraph rhythm.

Route the default through the shared `markdownProse` stacks instead, and cut
the app.css list rules down to the dash glyph so ordered and unordered lists
share Tailwind Typography's indentation and rhythm.

Fixes #10971

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

* fix(frontend): address review nits on the markdown prose fix

- default `GfmMarkdown` to the `sm` stack rather than `xs`: the AI-agent tool
  Message pane takes the default and has no ancestor font size, so `xs` left it
  smaller than its own label. The group note, whose wrapper is `text-2xs`, opts
  down explicitly.
- regenerate `static/tailwind_full.css`, which raw apps are served and which
  still carried the deleted list rules.
- correct the marker-color rationale: the typography config already maps markers
  to tertiary, so the rule steps them up rather than rescuing them.

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

* fix(frontend): make the note color override an arbitrary value

`text-inherit` is not generated: this config replaces the Tailwind color palette
outright and defines no `inherit` key, so `[&_*]:!text-inherit` compiled to
nothing and notes still rendered in the prose stack's `text-primary`. Verified in
the browser: a yellow note's list items now compute to `text-yellow-900`, matching
the wrapper and the edit-mode textarea, in both themes.

Also drop the `static/tailwind_full.css` regeneration. That file was generated with
tailwind 3.4.1 against a config predating the typography theme overrides; rebuilding
it today sweeps in 250KB of unrelated churn and would flip every raw app's `.prose`
palette from stock gray to Windmill tokens. Its staleness predates this PR and is
its own change.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 10:39:44 +00:00
GuilhemandClaude Opus 5 1901d3193b fix: keep the instance user editor popover inside the viewport (#10979)
* fix: keep the instance user editor popover inside the viewport

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

* fix: drop inert flex and min-h-0 classes from the user editor popover

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 10:38:44 +00:00
130a2f7408 feat: instrument sandbox isolation, data tables and in-flow script edits (#10981)
* feat: instrument sandbox isolation, data tables and in-flow script edits

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

* fix: address review findings on the new telemetry counters

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

* refactor: inline single-site telemetry helpers and trim what is collected

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

* docs: tighten the telemetry disclosure copy

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

* chore: update ee-repo-ref to 5921c03c8e28642efd1c390f590c0dab9834fa99

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

Previous ee-repo-ref: 548b5e0421a04a2d9a76cce6efc6c91b1d8560ee

New ee-repo-ref: 5921c03c8e28642efd1c390f590c0dab9834fa99

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-09-05 10:38:20 +00:00
8aab5034a6 feat: guest JWT entry for embedded apps (#10954)
* feat: guest JWT entry for embedded apps (jwt_guest_)

A second way in for a guest, alongside the signed-in guest session: a JWT the
embedding customer's backend mints and signs, verified per request against a
per-workspace key (a PEM public key or a JWKS URL), resolving to the same
seatless guest identity confined to the one app its app_path claim names.
Bearer prefix jwt_guest_, stateless (no token row). See PR #10954.

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

* feat: surface guest JWT as the embed method in the app deploy drawer

The deploy drawer explained the secret-URL embed but not the guest JWT path, so
the primary way to embed an app for a customer's own authenticated users was
undiscoverable. For a guest-mode app with guests enabled, show how to mint a
`jwt_guest_` token and append `guest.<jwt>` to the app URL, with a copyable
iframe template pre-filled with this app's workspace_id and app_path, and a note
that new guest emails are refused past the instance's free allowance (the live
count is shown just above).

Also log a guest JWT allowance refusal at warn, not info: the caller gets a bare
401 (the reason must not leak to an unauthenticated caller), so the log is the
admin's signal that the instance hit its guest cap.

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

* fix: correct the guest JWT minting instructions in the embed block

The block said "sign it with the workspace's guest JWT key", but that setting
holds the public verification key. Clarify the keypair relationship (configure
the public key or a JWKS URL in the workspace; sign with the matching private
key), name the accepted algorithms (RS/PS/ES; HS* refused), and keep the
required claims, so an embedder knows how to actually mint the token.

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

* feat: fall back to the instance JWT issuer for guest verification (off on cloud)

A workspace with no guest key of its own now verifies guest JWTs against the
instance issuer (JWT_EXT_JWKS_URL, already used by jwt_ext_), so an operator
running one issuer configures it once. Verification and the guest grant are CE;
granting a full login from that issuer stays EE (jwt_ext_, unchanged). Disabled
under CLOUD_HOSTED, where one instance issuer must not be trusted to mint guests
in every tenant's workspace — there the per-workspace key is the only source,
which also stays the override everywhere. The workspace settings note (hidden on
cloud) explains the fallback.

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

* fix: embed instructions cover both the workspace key and instance issuer

The embed block said to set the workspace's guest JWT key; now it says Windmill
verifies against the workspace key or, off cloud, the instance issuer
(JWT_EXT_JWKS_URL) when no workspace key is set. The instance clause is hidden
under isCloudHosted().

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

* fix: show the guest JWT embed block only when Embed is toggled

It belongs with the iframe snippet, not the plain-URL view, so gate it on
embedMode alongside the guest-mode / guests-enabled checks.

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

* fix: trust the instance issuer in the guest fallback; refresh stale docs

P1 (CI review): the fallback wrapped JWT_EXT_JWKS_URL as a workspace JwksUrl, so
it hit validate_guest_jwks_url and was refused for http/private issuers unless
ALLOW_PRIVATE_GUEST_JWKS_URLS was also set — a self-hosted internal issuer that
works for jwt_ext_ failed for guests, though the UI says setting the env var is
enough. fetch_jwks now fetches the instance issuer without the https/private
restriction (matching the jwt_ext_ loader; it stays operator-trusted), while a
workspace-admin URL is validated and pinned as before. All the size/key/URL
bounds still apply to both.

P2 (CI review): refresh the stale docs that said a missing workspace key always
refuses a guest JWT — the module, bearer, key-source, and EditGuestJwtKey field
docs now describe the workspace key with the off-cloud instance-issuer fallback.

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

* fix: fetch the trusted instance issuer like the jwt_ext_ loader

P1 (CI review): the instance-issuer fetch skipped SSRF validation but still
disabled redirects and default cert validation, so an instance issuer that works
for jwt_ext_ through a redirect or an operator-approved self-signed cert failed
the guest fallback. Fetch it with HTTP_CLIENT_PERMISSIVE (follows redirects,
honors ACCEPT_INVALID_CERTS) — the same behavior jwt_ext_ has — while a
workspace-admin URL stays validated, DNS-pinned and redirect-free. The body size
cap still bounds both.

P2 (CI review): the WorkspaceSettings field doc still said None/None means no JWT
guests; it now names the off-cloud instance-issuer fallback.

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

* docs: schema summary + OpenAPI cover the guest JWT columns and fallback

P2 (CI review): summarized_schema.txt was missing guest_activity.jwt_entry and
the two workspace_settings guest-JWT key columns (required by docs/validation.md
after a schema change). The edit_guest_jwt_key OpenAPI description now notes that
clearing the workspace key falls back to the instance issuer (JWT_EXT_JWKS_URL)
off cloud rather than necessarily stopping guest JWTs.

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

* fix: keep JWKS single-flight locks in a self-cleaning map, not a bounded cache

P1 (CI review): JWKS_FETCH_LOCKS was a 200-entry quick_cache. Past 200 cold URLs
it can evict a lock whose fetch is still in flight; the next request for that URL
then mints a fresh lock and starts a second fetch, so cycling configured
workspaces defeats single-flight and can storm the issuers. Replace it with a
plain map guarded by a JwksFetchLock RAII handle that removes each entry once its
last holder drops, so the map only ever holds the fetches in flight and never
evicts an in-flight lock. Add a unit test pinning the shared-lock and
self-cleaning invariants.

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

* chore: update ee-repo-ref to c2270eb5fe2d9f0968253e6b460c33186363f4e7

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

Previous ee-repo-ref: 5a1d9dee34159512c0823fddcd3d096490edbcce

New ee-repo-ref: c2270eb5fe2d9f0968253e6b460c33186363f4e7

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-09-05 10:23:37 +00:00
Ruben FiszelandClaude Opus 5 f977f5bf8b fix: stand the WAC park down for a cancel that beat it to the row (#10990)
* fix: stand the WAC park down for a cancel that beat it to the row

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

* refactor: share the cancel result payload with canceled_job_to_result

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 09:41:15 +00:00
Ruben FiszelandClaude Opus 5 54287102b2 fix: meter WAC compute per segment, not the whole sleep (#10985)
* fix: clear started_at when a WAC parent suspends

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

* fix: restore started_at on the WAC dispatch rollback, fail loudly on a no-op suspend

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

* fix: restore the pulled segment start on the WAC dispatch rollback

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

* feat: meter WAC execution per segment instead of only the last one

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

* fix: make the cloud feature self-sufficient per crate

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

* chore: name windmill-common/cloud directly in the worker cloud feature

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 11:11:29 +02:00
Ruben FiszelandClaude Opus 5 9d37b6f489 test: keep the mcp preprocessor header test off the dependency job (#10989)
Claude-Session: https://claude.ai/code/session_01YESK92Dtojyu4XMg19GHfp

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 11:10:41 +02:00
Ruben FiszelandClaude Opus 5 ebfac29096 fix: render the MCP OAuth consent page without a workspace (#10988)
Claude-Session: https://claude.ai/code/session_014EeEWKSqcnCEcPKe9uUuHC

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 10:05:15 +02:00
b5ae12bdf2 unbreak backend-test by bumping the ee ref past a test arity break (#10987)
* fix: bump the ee ref past the seats_consumed test arity break

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

* chore: update ee-repo-ref to fb1c5c109846d6c47aff70ab6cc631f4fd773678

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

Previous ee-repo-ref: d197b7b1c76e2aa7cde6cef2e2d9556607cce4c6

New ee-repo-ref: fb1c5c109846d6c47aff70ab6cc631f4fd773678

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-09-05 09:25:46 +02:00
Ruben FiszelandClaude Opus 5 1e31ab3a1e test: fit the relock no-op tests inside the 60s worker cap (#10984)
* test: fit the relock no-op tests inside the worker timeout

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

* test: share one wait budget in the relock no-op tests

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

* test: bound a whole relock wait on one deadline and idle the drain

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

* test: drop the redundant drain sleep in the relock no-op tests

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 09:23:50 +02:00
fce635d3c4 feat: guest app execution mode, a role that takes no seat (#10929)
* feat: guest app execution mode, a fourth role that takes no seat

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

* fix: make the guest grant a server-minted label, not a declarable scope

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

* chore: pin ee-repo-ref to the guest session companion branch

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

* fix: close the relabel hole, guest embed tokens, read-path switch, custom-path entry

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

* fix: guest tokens are not rescopable and guest embed tokens keep the sentinel

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

* fix: guest-derived tokens share one constraint set; gate sign-in on guest discovery

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

* fix: the label alone governs a guest; refuse guests with accounts; unserialize discovery

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

* fix: guest discovery fails closed; SAML aborts if the guest cookie write fails

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

* refactor: enforce the guest switch once at the auth door; sign-in for a guest of another app

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

* fix: guest app-mode decided once at the on-behalf resolver; clear a stale guest session before offering another app's sign-in

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

* fix: a guest may use anonymous apps; await the stale-session logout; trim comments

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

* fix: a guest's path confinement waits for the app's mode, so anonymous apps stay open to it

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

* fix: guest target survives http (Lax cookie), rides SAML RelayState; tell account holders on arrival

* fix: a guest uses an anonymous app as itself; S3 uploads confined by app mode

* fix: a guest upload needs an app policy; a missing app does not skip the confinement

* fix: guests are gated on the Enterprise plan server-side; pin ee-repo-ref

* fix: the guest plan gate fails closed on non-enterprise builds; settings report the effective switch

* fix: guest controls read the plan, not the key; gate the guest tests on the features they need

* docs: tighten the guest session invariant comments

* feat: 100 free guests per 30 days, then a quarter seat each on Enterprise and a hard cap elsewhere; superadmin guest list; refusals reach the page

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* fix: the cap is exact, an account ends a guest session at the door, popups close, and guest mode survives the CLI round trip

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* feat: a superadmin switch over guests for the whole instance; the pre-existing-user flag keeps its meaning

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* fix: drop the dead guest-access helper, name the instance setting once, guests tab states, CE save order

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* fix: a guest app path is refused at the mint if it could widen the scope; the instance toggle waits for its reload

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* fix: guests stop at the launched-by-me job grant; canonical app paths at the mint and discovery; the toggle ends on the stored value

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* fix: only the scope grammar's own characters bar an app path from guests, refused at deploy as well as at the mint

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* fix: the deploy-time guest path guard checks the destination of a rename and refuses a leading slash

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* fix: a workspace rename keeps the guest switch; the rename guard reads the deployed mode under the row lock

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* fix: guest_activity follows a workspace rename and goes with a workspace delete

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* chore: pin ee-repo-ref to the state-bound guest target

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* chore: pin ee-repo-ref; the guest cookie is never cleared by a callback

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* docs: the workspace-scoped guest_activity delete moves an instance-wide count; assert the mint records the guest

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* test: the seeded allowance is a day old, so only the mint can write today's guest_activity row

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BayTppRCstWX6qTf3LMco5

* chore: update ee-repo-ref to 1a10132e4f3cb442c7d0c2cf6e5d92d150bf6e07

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

Previous ee-repo-ref: 32841072aa396bff91d30bd91854fa348cb3c439

New ee-repo-ref: 1a10132e4f3cb442c7d0c2cf6e5d92d150bf6e07

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-09-04 22:47:28 +02:00
Diego ImbertandClaude Opus 5 c6c26fbd92 fix(datatables): a permissioned data table cannot be cloned into a fork
A clone lands in a brand-new database where none of the roles exist, and
the fork's copy of the config is stripped of its permissions — so every
member of the fork, including one whose role reached two tables, resolved
to the copy's own owner connection and read the whole thing. The fork also
inherits every member of the parent, so it took one admin clicking "clone
schema and data".

Refused at the two clone endpoints and at the fork wiring itself, which is
the one a caller cannot go around. A plain database-to-database import is
untouched: it lands nowhere that strips permissions.

Sharing the original is the supported answer, and carrying the parent's
restrictions into a shared data table is the next commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 22:06:04 +02:00
Diego ImbertandClaude Opus 5 1e6d28def2 fix(datatables): a data table that keeps its database keeps its roles
The fork-database drop cleared the permissions block and destroyed the
logins above the guards that refuse the drop, so a refusal — or a DROP
DATABASE that fails on an open session — left a live data table
unpermissioned, and every member of the workspace resolving to the
connection that owns it. The clear now happens only once the drop is
under way, and is put back if the drop does not happen.

The workspace-deletion snapshot takes the settings row first, so a
permissions save cannot add a login between the read and the row's
deletion.

The raw-app data drawer waits for the caller's usable roles before
mounting the content, like its sibling in the DB manager: mounting is
what fires the schema and metadata queries, and a first round sent
without a role runs — and caches — as whatever the server defaults to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 20:08:14 +02:00
Diego ImbertandClaude Opus 5 95c3059266 fix(datatables): what disappeared is read from the config, and its logins go first
The save planned drops from the request's `deleted_datatables`, so a config that
drops one name and adds another — no rename declared, no deletion declared —
left the first one's logins for whatever data table is created under that name
next. What disappeared is the difference between the two configs.

Dropping a fork's data table databases dropped them before anything asked about
their logins, which are cluster-wide and outlive the database. The permissions
block is cleared first, both because a data table whose database is going has no
business claiming roles in it and because that is what the drop reads to know
they are nobody's.

The raw app's tree is asked for as the role the drawer is using, like its
sibling: it was always the default role's view, under a picker that changes the
role.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 19:08:00 +02:00
Diego ImbertandClaude Opus 5 ec19b0fe21 fix(datatables): a generated login belongs to one data table in one workspace
Three ways it could stop being true, all of them ending with a login shared
between two data tables or outliving every record of itself:

A rename carried the roles verbatim, and their names are made from the data
table's name — so a data table created under the name that was left behind
generated those same names, adopted those logins and reset their passwords.
Renaming a permissioned data table is refused, the way pointing one at another
database already is.

Deleting a workspace removed the settings that name its logins without dropping
them. What to drop is resolved while that row is still there and run after the
commit, like a data table's own deletion — a workspace id is reusable, so what
survived was adoptable by the next workspace of that name.

Generating the initial migration takes a `pg_dump` with the data table's own
connection and returns every object in it, on nothing but `ApiAuthed` — a member
who may run as no role could read the whole schema through it. It answers to the
gate the rest of the migration management does.

The raw app's data drawer asks for its tree when it opens, like the manager's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 18:50:00 +02:00
Diego ImbertandClaude Opus 5 443d8a5316 fix(datatables): the manager reaches every database only when it is open
This PR swapped the drawer's data table list for one that reports each table's
schemas and privileges, which connects to every configured database in turn —
and the drawer is mounted on every logged-in page, so that ran on page loads
with the manager closed, up to a connection timeout per unreachable database.
It is asked for when the drawer is open.

The raw app's data drawer keeps a role across sessions while both entry points
replace the data table under it, so browsing one table as `analyst` and then
opening another queried that one as its own `analyst`, or as a role it does not
have. Both clear it, like the in-drawer switch already did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 18:08:23 +02:00
Diego ImbertandClaude Opus 5 3de44a8f8d test(datatables): pin which resolutions prove the database, not the comparison
The test added with the last commit called the comparison directly, and that
never took a role — moving the call back inside the branch that swaps credentials
would leave it passing. The rule is a function now, and asserting it fails when
the rule is reverted: an identified caller of a permissioned data table proves it
whatever role it lands on, the machinery that repairs one does not, an
unpermissioned one has nothing to prove.

The exemption's comment names what `Unchecked` actually covers. Two of its
callers are not repair: the admin diagnostic, and the postgres trigger's
replication connection — which reaches the data table's own connection the way
every caller did before roles existed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 17:51:46 +02:00
Diego ImbertandClaude Opus 5 9580e9985a fix(datatables): admin follows the same record as every other role
`admin` has no login to swap in — it is the data table's own connection — so the
check that the data table still points where its roles live sat inside the branch
that swaps credentials and never ran for it. A caller granted `admin` was handed
whatever the resource pointed at, which is the case the record exists for: the
guards on the resource can be gone around through a `$var:` or a concurrent
enable. It runs for every permissioned data table now, whatever role the caller
lands on, and only for callers with an identity — the machinery that repairs a
moved data table resolves as admin and has to keep working, which is what
re-saving its permissions does.

The disabled schema toggle's tooltip names the specific refusal first, like the
hint beside it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 17:40:30 +02:00
Diego ImbertandClaude Opus 5 1b82a7c236 fix(datatables): the specific refusal is the one worth saying
With no usable role the access question goes out role-less, which the server
answers as the data table's default — the role that was refused — so the entry
comes back with an error and both refusals are true at once. The one that names
the cause is tested first.

`_format_py_params` is now what its comment claimed: positional-only parameters
keep their `/`, their defaults line up with the positional ones, and the second
renderer is gone rather than kept one clause behind. Every generated file comes
out byte-identical, since nothing in the SDK uses `/` yet — which is exactly how
the missing `*` survived.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 17:20:23 +02:00
Diego ImbertandClaude Opus 5 7cd6e081a7 refactor(prompts): one renderer for Python signatures
`extract_py_functions` inlined a third copy, one clause behind the two functions
that already do this — it lost the bare `*` until last commit, and still dropped
positional-only arguments and annotated varargs. It calls the shared one; the
generated files come out byte-identical.

The picker says which of the three refusals it hit, including the database it
could not reach, and an app that keeps no data table no longer creates a schema
in one: the app would never refer to it, and where the database is unreachable
the failure is one the user cannot act on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 17:09:35 +02:00
Diego ImbertandClaude Opus 5 8a40ff46fd fix(prompts): a keyword-only argument is advertised as one everywhere
The general Python extractor dropped the bare `*` the same way the datatable one
did, so every keyword-only signature it emits — `datatable`, `task` and the rest
— told an agent to pass those arguments positionally, which is a TypeError.
Regenerated: the script prompt, the Python skill and the CLI guidance carry the
separator now.

The fork dialog says both outcomes where the permission check did not answer:
what the backend does with the data table is decided by the config, so a label
promising the original is kept is a promise it cannot make.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 16:59:07 +02:00
Diego ImbertandClaude Opus 5 b1474820b5 fix(datatables): a database that could not be reached is not a role that refused
`can_create_schema: false` on an entry the backend kept with an error says
nothing about the role, but the modal read it as one: it left `New`, could not be
put back, and the tooltip blamed the role for a database it never reached. The
access answer carries its failure, the flip waits for an answer, the tooltip says
what actually happened, and an app that would name that data table waits with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 16:52:01 +02:00
Diego ImbertandClaude Opus 5 93590da9d8 fix(datatables): a lookup that failed is not an answer
An empty role list meant "nothing to pick" whether the endpoint said so or never
answered, so a failed lookup on a permissioned data table let an app be created
naming it with no role — the case the check exists for. The failure is carried,
and an app that would name the data table waits for an answer.

The fork dialog said "Keep original" on the same failure, for a data table the
backend drops from the fork when its permissions are on: it says the check did
not answer instead of asserting the outcome.

The generated Python signature keeps the bare `*`. `datatable(name, *, role)` is
keyword-only, and an agent following the advertised positional form gets a
TypeError.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 16:38:11 +02:00
Diego ImbertandClaude Opus 5 7c68818da1 fix(datatables): a role picked at creation is the role the app keeps
Leaving it out when it happened to equal the data table's default meant "whatever
the default is then", so an app created as `analyst` became a `reader` app the day
an admin changed that default. The pick is written down.

The hint says what is true of the choice in front of it and no more: what the
assistant writes when an app names no data table is a property of the prompt, not
of this data table.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 16:32:42 +02:00
hugocasaandClaude Opus 5 f037c73d10 feat(frontend): group the agent form and edit saved agents as drafts (#10880)
* feat(frontend): group the AI agent step form and edit saved agents in a modal

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

* feat: edit a saved AI agent through its own resource draft

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

* refactor: drop the agent fork-for-edit session now that edits live in a draft

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

* feat: edit ai_agent resources from the resources page with the agent editor

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

* fix: send a standalone agent's brain from the module when testing a step

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

* fix: keep the agent draft faithful to the resource it deploys to

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

* chore: add the sqlx cache entry for the eval subject rename

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

* refactor: share the module insert between the graph and the agent editor

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

* feat(frontend): open evals inside the agent editor, actions in its header

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

* feat(frontend): add tools from the agent editor and lighten its test pane

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

* fix(frontend): open an ai_agent deep link in the agent editor

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

* fix(frontend): drop the failed result badge on a step that never ran

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

* feat(frontend): head the agent editor's levels with a back control

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

* feat(frontend): drop connect and fill inputs from the agent editor

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

* feat(frontend): lighten the agent editor's run panel

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

* fix(frontend): stop a nested agent tool's config reading as AI-filled

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

* feat(frontend): offer only AI or static on an agent tool's inputs

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

* feat(frontend): give a saved agent's tool editor a static-only surface

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

* feat(frontend): open an agent tool in a drawer beside the agent

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

* feat(frontend): hide unset agent config in the run form

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

* refactor(frontend): share the input forms' pickers and s3 lookup

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

* chore(frontend): drop a dead agent-editor export and fix two stale comments

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

* fix(frontend): reach an ai_agent's resource-level settings and copilot

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

* fix(frontend): open an ai_agent's resource view as JSON, not the generic form

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

* fix(frontend): address review findings on the agent editor's draft and streaming

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

* fix(frontend): close the agent editor on a version restore, as the resource editor does

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

* fix(frontend): stop the provider picker auto-writing a kind, and clear review nits

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

* refactor(frontend): drop the fork-for-edit leftovers from the agent card

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

* fix(frontend): mount the agent editor in the dev flow editor and guard the deep-link race

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

* fix(frontend): deploy the agent config that was submitted, and refuse one no run could use

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

* refactor(frontend): build the agent editor's rows from the design-system button

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

* fix(frontend): keep a draft-only agent's draft, and let a blank MCP summary deploy

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

* fix(frontend): guard read-only agents, incomplete MCP tools and duplicate editor mounts

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

* fix: read-only agent editor, linked-card refresh, atomic eval rename

* fix: eval rename needs the privileged pool, per-workspace write access

* fix(frontend): drop the agent editor target when its mount goes away

* refactor: drop the agent rename work from this PR, unban the bindable defaults

* fix(frontend): refuse a renaming deploy and drop the copilot from static-only fields

* fix(frontend): mirror the worker's streaming rule and scope agent writes to their target

* fix(frontend): read runtime streaming as off and reset the drawer's json view

* fix(frontend): read an unsettled output_type as non-streaming too

* fix(frontend): let the showing modal claim an agent opened from inside it

* fix(frontend): keep in-flight edits, tool replacements and every linked step in sync

* fix(frontend): keep attachments in the run form and bind the agent ref to its tools

* fix(frontend): preview the agent as authored and re-evaluate step args on run

* fix(frontend): scope agent-editor ownership to the flow's workspace

* fix(frontend): drop the tool drill-in where there is no graph to select on

* fix(frontend): require a provider kind and keep one resource editor open at a time

* fix(frontend): keep legacy nulls, static-only text literal, and the handover anchor

* test(worker): pin the agent streaming default

* fix(frontend): let an AI-fillable input be switched to static

* fix(frontend): report agent editor background failures instead of floating them

* fix(frontend): keep the version pane's path alive while the editor closes

* fix(frontend): clear the anchor-keep flag at the start of each drawer session

* fix(frontend): preview the agent without its synthetic path, refresh the baseline on external writes

* refactor(frontend): drop the unverifiable baseline refresh, state the synthetic-path rule

* fix(frontend): keep the synthetic path out of agent tool test runs too

* refactor(frontend): host the agent editor under the agent's own path

* fix(frontend): mark an agent editor's host explicitly instead of inferring it from the path

* fix(frontend): discard linked-agent responses from before a deploy

* fix(frontend): keep a flow mount from claiming an agent editor's nested target

* feat(frontend): keep an agent used as a tool inside the agent being edited

* fix(frontend): reserve the agent editor's root module id

* docs(frontend): record why the agent editor previews under the agent's path

* fix(frontend): refuse to open or deploy a resource that is not an agent

* docs(frontend): put the scope-migration comment on the function it describes

* fix(frontend): refuse an agent path whose resource type is not proven

* fix(frontend): recheck the resource type before deploying, and keep expressions off static-only inputs

* fix(frontend): lazy-load the agent editor and slide its levels like the evals pane

* refactor: drop unreachable non-list tools check from agent deploy

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

* fix(frontend): clear text-only agent fields on image output, reserve the root id

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

* fix(frontend): keep the agent editor usable for a non-list tools value

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

* fix(frontend): stop the parked eval run list from taking arrow keys

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

* fix(frontend): report a non-list tools value on deploy instead of throwing

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

* fix(frontend): keep temperature editable for image output

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

* fix(frontend): skip non-object tool entries when rendering an agent

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

* fix(frontend): guard tool entry reads instead of copying the tool array

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

* fix(frontend): key tool rows by position so duplicate ids render

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 16:32:17 +02:00
Diego ImbertandClaude Opus 5 a253ba3d11 fix(datatables): the hint states the situation rather than a way out that is not one
Turning table creation off lets the app be created, but it does not stop the
assistant writing queries against the data table: with none named, what it writes
falls back to the workspace's default — the same one the caller may run as no
role on. The hint says what will happen instead of recommending an escape that
leads back to it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 16:21:34 +02:00
Diego ImbertandClaude Opus 5 6608180e3e fix(datatables): the default-database popover waits for the answer too
Its Schema select read the access resource without the stamp its sibling reads,
so switching the database one row above left the previous data table's schemas
listed and clickable — a schema name the new data table may not have, written
into the app's policy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 16:12:32 +02:00
Diego ImbertandClaude Opus 5 bdcd42ac0a fix(datatables): only the app that would name the data table is refused
A caller who may run as none of a permissioned data table's roles could not
start anything: the refusal ignored whether the app was going to name it at all.
With table creation off nothing saves the data table, and an app that never
touches it is not theirs to be stopped over — so the buttons look at that, and
the hint says what the way out is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 16:02:09 +02:00
Diego ImbertandClaude Opus 5 4638f35951 fix(datatables): a workspace with no data tables has nothing to wait for
The gate that keeps the modal from deciding before an answer arrives never
settles where there is no question: with no data tables, nothing is selected, so
both Start buttons stayed disabled under an alert saying an app can still be
created. Waiting is for a data table that was asked about.

A data table this caller may run as no role on is refused instead: leaving the
role out saves its configured default, which is the one they cannot use, so the
app would be created with queries that are refused. The picker says so where the
data table is chosen.

The six rounds of fixes around the loading state left three comment blocks two
paragraphs deep, one of them documenting the item below it. The rule they all
restate is written once, where a reader would break it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 15:53:30 +02:00
Diego ImbertandClaude Opus 5 1e2052d74a fix(datatables): undefined on both sides is not an answer
Before auto-select lands there is no data table to have asked about, and the
initial value stamps none either, so the gates added to keep the schema mode from
flipping against a non-answer compared `undefined` with `undefined` and let it
through at mount — the modal opened on `None` rather than the `New` it declares,
and an app started without touching the toggle got no schema. Settled now means a
data table is selected, the role list answers for it, and the access answer was
asked as the role the app will run as.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 15:31:19 +02:00
Diego ImbertandClaude Opus 5 3d2fa49358 fix(datatables): the role an app can use is the one it saves
Hiding the picker where there is one usable role meant saving none, and no role
means the data table's configured default — which is not filtered by what the
caller may use. A member whose only role is `admin` on a data table defaulting to
`analyst` got an app whose queries are refused. The role is saved whenever it is
not what the data table would resolve to anyway, and the schemas are asked for as
that role whether or not the picker is shown.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 15:21:10 +02:00
Diego ImbertandClaude Opus 5 2d8a99d2a3 fix(datatables): an answer about another role settles nothing
What a role may create in is the question the access resource asks, so an answer
computed as a different one is not an answer to it — and there is always one of
those: the role getter is empty until the role list settles, so every switch
fetches once with no role, which the server reads as the data table's configured
default. That answer stamped the data table alone, passed the check, and its
one-way flip took the modal off `New` for a role that can create schemas.

Both halves of the key are stamped and compared now, and Start waits for that
answer too: a new schema name checked against a list from another question is
not checked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 14:49:51 +02:00
64b6798799 fix: name the extension to load when duckdb autoload hits the fence (#10972)
* fix: tell duckdb scripts which extension to name when autoload hits the fence

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

* chore: update ee-repo-ref to b9aeffa83f0e601f123c7eab536b235719786da1

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

Previous ee-repo-ref: fd196f99e22205c69946870997dadd921847cc97

New ee-repo-ref: b9aeffa83f0e601f123c7eab536b235719786da1

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-09-04 14:43:42 +02:00
Diego ImbertandClaude Opus 5 8868c5ff72 fix(datatables): nothing is decided before the answer for this data table arrives
Starting a new app while the role list loads saved no role, which the server
resolves to the data table's configured default — a role the caller may not be
allowed to use, where a moment later the picker would have chosen one they can.
Start waits for the list that answers for the data table selected.

The schemas and the right to create one get the same stamp the role list has. At
mount they are the initial value rather than an answer, and the effect read that
as "this role cannot create schemas", flipping the modal off `New` with nothing
to put it back; during a switch they are the previous data table's.

A role drop that fails names what it leaves behind whichever statement failed:
the config that stopped naming them has committed either way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 14:41:11 +02:00
Diego ImbertandClaude Opus 5 1e3e08566d fix(datatables): a row action names the data table it was asked for
A pending action kept only what to do, so a switch that landed anywhere else —
the target failed to load, or the user moved on — ran it against whatever the
manager was connected to by then. Dropping a schema is not a thing to do by
approximation: it carries its data table and is discarded when that is not where
we are.

The new-app picker reads the role list only once it answers for the data table
selected. During a switch it offers nothing rather than the previous table's
roles, and an app created in that window gets the new table's default instead of
a name carried over.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 14:17:35 +02:00
Diego ImbertandClaude Opus 5 2257b05b28 feat: make S3 permission rules reorderable by drag and drop (#10958)
Claude-Session: https://claude.ai/code/session_01DkKR3V3rWDyh1tDZCxmGLT

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 13:58:26 +02:00
Alexander PetricandClaude Fable 5.1 d232d57f0d offer known Google scopes as checkboxes in the oauth connect dialog (#10945)
* feat: offer known Google scopes as checkboxes in the oauth connect dialog

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kv72vdCDggCZEjJSmNCwnX

* fix: keep custom oauth scope rows apart from checked options while typing

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kv72vdCDggCZEjJSmNCwnX

* fix: drop the rust scope_options field and render checkboxes from the ticked set

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kv72vdCDggCZEjJSmNCwnX

* fix: keep ticked oauth scope options independent of free-text rows

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kv72vdCDggCZEjJSmNCwnX

* fix: toggle oauth scope checkboxes from component state, not the reverted input

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kv72vdCDggCZEjJSmNCwnX

* fix: keep the legacy gforms default scope so pre-migration accounts still refresh

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kv72vdCDggCZEjJSmNCwnX

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 13:55:20 +02:00
Diego ImbertandClaude Opus 5 7f09781926 fix(datatables): the role list answers for the data table it was fetched for
Switching the new-app picker to another data table leaves the previous one's
roles in hand until the request lands, so resetting against them chose that
table's default and recorded the choice as this table's — and a role name the two
share survived the switch. The list now says which data table it answers for, and
the picker waits for the one it asked about.

The fork dialog says "Not shared (permissions enabled)" where it used to say
"Keep original", which for a permissioned data table now means it is absent from
the fork. The listing carries that state so it cannot pair with another
workspace's data tables.

A failed drop names only the roles it actually tried; the ones the config named
again were skipped, and the caller could not tell them apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n
2026-09-04 13:41:28 +02:00