`abandon()` stops the run at the next phase boundary; the request already
sent still lands. Reloading the list at that moment could read it before
that write committed, leaving the caller stale again — the thing the
reload was added to fix. It now waits for `running` to clear, which is
immediate for the common case of dismissing a finished import, with a
cap so a run that never settles still ends in a reload.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
Closing a landed import with the X left the home list stale: only
`finish()` reloaded it, so a workspace that now holds a project kept
showing its placeholder rows. A run that started wrote items whether it
finished, was abandoned or failed partway, so any dismissal after one
reloads.
Creation reported failure for a failed *list refresh* too, and handed
the form back — where a retry picks the next free id and creates a
second workspace. Once `createWorkspace` returns, nothing may report
failure: the refresh is logged if it fails, and the hand-over proceeds,
since the workspace is real either way.
The disabled-link tooltip also claimed the settings could not be read
during the ordinary load, before anything had failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
Create was gated on knowing whether the instance derives usernames, and
the link beside it went to a form with no such gate — so the way around
the block sat next to it. It is disabled until the policy is known, with
a title saying why.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
There is no safe default. `create_workspace` refuses a username on an
instance that automates them and requires one on an instance that does
not (`workspaces.rs:5820`), so a client that cannot read the setting has
two request shapes available and the server rejects both. Last round's
"ask for one" was as wrong as the "automated" guess it replaced.
So the loader reports the failure instead of inventing an answer, and
the form says so: Create stays disabled, with a line explaining why and
a link to try again. Verified in the browser both ways — unreadable
policy disables Create and shows the message, a healthy load prefills
the name and enables it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
Three rounds of this bug moved between call sites because the shared
loader rejects when it cannot read `automate_username_creation`, leaving
each caller to guess — and both guessed "automated", which hides the
username field and posts none to an instance that derives none.
`loadUsernamePolicy` now answers "ask for one" in that case, so
`SimpleCreateWorkspace` and `CreateWorkspaceInner` both render a field
someone can type into rather than submitting a guess. An instance that
does automate ignores a username it was sent, so asking is safe either
way.
The prefill and the policy are settled apart now too: a failed
`globalWhoami` costs the suggested name and nothing else.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
`policyLoaded` was set in a `finally`, so a failed policy load unblocked
the form with `automateUsername` still at its default — the exact submit
the flag exists to prevent. The failure now hands over to the full form,
which asks for a username outright rather than inferring one, so the
flag is never true while the answer is still a guess.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
`load()` assigned the suggested name unconditionally, so a name typed
while its two requests were in flight was replaced a moment later. It
now yields to anything already typed.
Nothing may be submitted before the username policy lands either:
`automateUsername` starts at the common case, and posting that guess to
an instance that derives no usernames sends none where one is required.
`policyLoaded` gates both the button and `create()`, and is set in a
`finally` so a failed load leaves the form usable rather than wedged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
The telemetry doc's tally is maintained by hand and main had just moved
it; this PR adds a feature, so it reads 48 across eighteen with `home`
in the list — verified against the pinned EE ref rather than counted by
eye.
The empty state's comment narrated a preload that no longer exists and
the defects it caused. What a future reader needs is the constraint:
`disable_hub` loads asynchronously, so a fetch from here goes out before
the setting forbidding it is known.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
Warming the hub catalogue when the empty state rendered bought the time
between the caption appearing and someone clicking it, and cost two
defects: the request fired on instances with the hub turned off, and the
gate added for that raced `disable_hub`'s own load, which starts false
and stays false if the settings request fails.
The picker fetches on open instead. Measured: nothing before the click,
one request after it, 411ms to a filled list. `disableHubStore` still
hides the link and the menu entry, which cost no request and correct
themselves if the setting lands late.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
An instance with the hub turned off still got the catalogue preloaded on
every empty home and an "Import a hub project" entry in the create menu
— an outbound request the operator has said not to make, and a door to
somewhere unreachable. Both now observe `disableHubStore`, the store the
script and flow hub pickers already read. With the hub off the caption
reads "Create a new one." rather than continuing a sentence whose first
half is gone.
The telemetry disclosure also scoped the create menu and picker counters
to the empty home, when both fire from the toolbar in a populated one,
and said a creation was recorded when what is recorded is the menu
opening.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
"Empty" here means the default listing found nothing, and a workspace
whose items are all archived looks exactly the same. The searchbar
carries "Only archived", so taking it off the pointer left those items
unreachable without hand-writing a query URL. Dimmed still, never
`inert`.
The disclosure named the counters that fire on a creation or an import
and not the three that fire on merely seeing the empty home or opening
either picker.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
The note is about landing on top of what a workspace already holds, so
it belongs wherever the destination is an existing workspace. The route
already read it that way; the dialog, which always imports into the
current workspace, was hiding it. It costs one collapsed row.
`onCreated` was typed as taking the new workspace id, and the advanced
branch passed `''` because `CreateWorkspaceInner` does not report one.
No caller reads it — the form has already switched to the workspace by
then — so the argument goes rather than the lie staying.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
* fix: write and read python job files as utf-8, not the platform locale
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ErBZtEpLpkFi1Y1W6eNZBE
* refactor: trim the PYTHON_UTF8_ENVS comment to the 4-line limit
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ErBZtEpLpkFi1Y1W6eNZBE
* chore: bump ee ref for the python runner-group utf8 companion
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ErBZtEpLpkFi1Y1W6eNZBE
* chore: update ee-repo-ref to d33ea730c550cdbc7d050aeb6d40dcef3d134e07
This commit updates the EE repository reference after PR #782 was merged in windmill-ee-private.
Previous ee-repo-ref: c8318661f8d91da9172a3c2dca050b70ba7afda2
New ee-repo-ref: d33ea730c550cdbc7d050aeb6d40dcef3d134e07
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>
* 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>
* 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>
* 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>
* 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>
`usr.username` is VARCHAR(50) while the provider name and email it is
derived from run to 255, and `create_workspace` inserts the value
untruncated — so a long first name failed the same way the invalid
characters did: posted invisibly, refused on insert, with nothing on
screen naming the field. Undefined instead, which the form already
routes to the full one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
* 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>
`loadUsernamePolicy` derived a username by stripping dots, so `O'Connor`
and `alice+demo` both produced values the `proper_username` constraint
refuses — posted invisibly by the simple form, which then failed with
nothing on screen explaining why. `usernameFromName` keeps only `[\w-]`
and answers undefined when nothing usable is left, which is already the
form's cue to open the full one.
The credentials step offers Finish when the export could not be read,
since it cannot tell what is outstanding — and that landed in
`template_setup` as `filled`, the bucket meaning the step came out
clean. It reports whether it checked anything, and an unread step counts
as `unchecked`.
Also drops an orphaned `.sqlx` entry left by the create-at-signup query
this branch abandoned, and rewrites the stepper's first-frame comment,
which argued from a meaning of `resourceCount` that main has narrowed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
* 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>
`template_abandon` folded a landed import closed with the X into `idle`,
the bucket read as "opened this and bounced" — three outcomes in one
number. It gets its own `done` stage.
`template_setup` counted skipped rows through `value`, which is an
increment: `skipped` accumulated rows while `filled` and `none` counted
imports, two units in one counter with no way to recover one from the
other. The row count becomes a bucket in the key, so every event is one
import and the buckets compare.
The import dialog also asked for the hub URL settings at init, and the
home list now mounts it for everyone on every arrival — two GETs for a
string only the project card renders. Deferred to the first pick.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
The picker only existed inside the empty state, which disappears as soon
as a workspace holds one item — nothing else in the product linked to
`/projects/import`. New → Import now offers a hub project, opening the
catalogue in a dialog: a popover anchored to an item inside an open
dropdown leaves two melt layers arguing over focus. The list and the
import dialog move up to ItemsList, so one dialog serves both doors.
`template_setup` records how the credentials step ended — `filled` only
when nothing was outstanding, `skipped` carrying how many rows were left
— and `template_abandon` records where a dismissed import was given up.
`template_picker_open` gains a key naming the entry point.
Also on the workspace picker: logging out is a text link on the line
that says who you are and an item in the settings menu, rather than the
page's accent action, and onboarding's Previous joins the row it belongs
to instead of hanging under the button that finishes the form.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
* chore: stop denying reads of secret files in claude settings
Any Read() deny rule makes Claude Code resolve the file operands of every
Bash command that reads files. A path it cannot resolve, such as one that
follows a cd into a directory the analyzer does not track, escalates to a
permission prompt even under bypassPermissions. A plain recursive grep in
the repo root escalates too, because it could reach .env.
Drop the read rules and widen the write rules to cover the same files, so
secrets still cannot be written through Edit, Write, or a shell redirect.
Reads of those files are no longer blocked.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RNCupPk2yewQT1JMNjkV8M
* fix: keep the sso group reconciler alive in oauth2-less builds
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W24T1FjQXQ87AoeC3UxWWC
* chore: update ee-repo-ref to d6297e6844dc2aab4745fce328e32ccab508969f
This commit updates the EE repository reference after PR #777 was merged in windmill-ee-private.
Previous ee-repo-ref: eec88486fb2df0ba15998ef285f52fc67af90b1e
New ee-repo-ref: d6297e6844dc2aab4745fce328e32ccab508969f
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>
Any Read() deny rule makes Claude Code resolve the file operands of every
Bash command that reads files. A path it cannot resolve, such as one that
follows a cd into a directory the analyzer does not track, escalates to a
permission prompt even under bypassPermissions. A plain recursive grep in
the repo root escalates too, because it could reach .env.
Drop the read rules and widen the write rules to cover the same files, so
secrets still cannot be written through Edit, Write, or a shell redirect.
Reads of those files are no longer blocked.
Claude-Session: https://claude.ai/code/session_01RNCupPk2yewQT1JMNjkV8M
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: add sso_groups_claim setting for login-time instance group sync
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YESxWqzt959S6TY6vbc4eG
* chore: bump ee-repo-ref for the SSO groups claim reconcile
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YESxWqzt959S6TY6vbc4eG
* chore: update ee-repo-ref to 3b89bfc11314a326a191101cfe3ef65f6f7f82a8
This commit updates the EE repository reference after PR #774 was merged in windmill-ee-private.
Previous ee-repo-ref: e388527f9adbbe466fe050ca8d1d236ce3342bc3
New ee-repo-ref: 3b89bfc11314a326a191101cfe3ef65f6f7f82a8
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
`done` survives a retry, so Finish is clickable while the run is going
again, and its own closing reaches the same falling edge the X does.
Abandon the run either way; say it was stopped only when that is what
the click asked for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
Modal reports dismissal only through its bindable `open` — the X, Escape
and the backdrop dispatch neither `confirmed` nor `canceled`. Bind it, so
clearing `pick` follows the dialog closing: re-picking the same project
opens it again, and a run still in flight is abandoned with a toast
instead of writing to the workspace with no UI in front of it.
Also mark the inline-link buttons as sanctioned rather than oversights,
and give Log out `text-accent` instead of `text-blue-500`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
Modal reports dismissal only through its bindable `open` — the X, Escape
and the backdrop dispatch neither `confirmed` nor `canceled`. Bind it, so
clearing `pick` follows the dialog closing: re-picking the same project
opens it again, and a run still in flight is abandoned with a toast
instead of writing to the workspace with no UI in front of it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
Added for the template picker, which turns out not to need it: DataTable's own
container is already `h-full`, so `containerClass="h-full"` merged to nothing and
the height the list pages against comes from the flex chain above it. A prop with
no effect at its only call site is public surface for free. InfiniteList is back
to what it was.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
The popover's default positioning caps its height to the viewport, and the list
inside it carried a fixed one, so a capped box overflowed its own frame — visible
with the AI composer hidden, where the caption sits high and `placement: top`
left almost no room above it. It opens downward now, with flip fallbacks, at a
definite `min(72vh, 520px)`; the list fills what the header leaves, which is
still the definite height it needs to page.
`creating` on the create form becomes `onCreatingChange`: `$bindable(default)` on
an optional prop is banned, and this is something the form reports rather than
state it shares.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9