Commit Graph

3364 Commits

Author SHA1 Message Date
Alexander Petric a89fcf72fa [ee] feat(scim): OAuth2 client-credentials grant for SCIM provisioning
Support OAuth 2.0 client-credentials authentication for SCIM provisioning
(e.g. Microsoft Entra ID) alongside the existing static bearer token, so
identity providers can use short-lived, rotatable access tokens.

Backend (EE companion PR modifies scim_ee.rs):
- Unauthenticated token endpoint POST /api/scim_token/token issues a
  short-lived scope:scim JWT via the client-credentials grant.
- has_scim_token validates SCIM JWTs (when OAuth is configured) in addition
  to the static token — fully backward compatible.
- Super-admin config endpoints (generate/rotate/disable) store the client
  secret hashed (SHA-256). New scim_oauth global setting is agent-worker
  blocked and live-reloaded.

Frontend:
- OAuth 2.0 client-credentials section in the SCIM/SAML instance settings:
  enable toggle, generate-secret (shown once), copyable token endpoint and
  client ID, regenerate/disable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:36:08 +00:00
Ruben Fiszel 710a13a59d fix(apps): cover script/flow component outputs in deployed-app S3 provenance gate (#10070)
* fix(apps): cover script/flow component outputs in deployed-app S3 provenance gate

Deployed apps read S3 files on-behalf of the app author for logged-in viewers
(#10048). A confused-deputy guard confines those reads to files the app
"produced", but the recent-production check only matched inline `appscript`/
`preview` jobs nested under the app path. Files produced by the deployed
script/flow components an app is wired to run (e.g. a SQL query persisted to S3)
were therefore denied "File restricted" for every viewer, admins included.

Expand the provenance check to also match completed `script`/`flow`/`flowscript`/
`flownode` jobs whose `runnable_path` is one of the app's declared triggerables,
and accept the author identity via `permissioned_as = on_behalf_of` (not only
`created_by = caller`) so files produced on-behalf of the author are covered.
Reads outside the app's declared triggerables stay denied.

Adds a regression test seeding a script-kind produced file that reproduces the
"File restricted" denial before the fix and passes after.

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

* fix(apps): key S3 provenance on on-behalf identity + cover flow steps (review)

Addresses the CI review on the S3 provenance gate:

- P1 (confused deputy): the recent-production check keyed on `created_by =
  caller`, so a viewer who can run a declared script/flow directly (outside the
  app, with un-pinned inputs) could craft a result naming an author-only key and
  read it back through the app as the author. Key provenance instead on the
  producing job's `permissioned_as` matching the on-behalf identity the download
  reads as (the author in author-mode); a viewer's direct run has
  `permissioned_as = viewer` and no longer clears the gate. Drops `created_by`
  from both the appscript/preview and script/flow branches, closing the same
  latent hole in the pre-existing inline-script branch.

- P2 (dead flow-step branch): `flowscript`/`flownode` jobs have
  `runnable_path = <flow_path>/<step_id>`, which exact `= ANY(...)` never matched.
  Split script vs flow triggerable paths; flow kinds now match the flow's own job
  (bare path) and its step jobs via a `<flow_path>/%` prefix, bounded to declared
  flows.

- P2 (test realism): the regression test now uses the production
  component-prefixed triggerable key format (`<id>:script/...`), exercises a
  flow-step-produced key, and asserts a viewer's own direct run of a declared
  script stays denied (the P1 case).

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

* fix(apps): tie deployed-app S3 provenance to an app-origination marker (review)

Second CI-review round flagged that `permissioned_as` still does not prove a job
was app-launched: a runnable configured with its own `on_behalf_of` makes a direct
`/jobs/run` resolve `permissioned_as` to that identity (the app author), so a viewer
with run access could execute a declared runnable directly, craft an S3 result, and
read it back through the app. The flow-path `LIKE fp || '/%'` match also let `_`/`%`
in a declared path admit unrelated flows.

Introduce a real app-origination marker instead of inferring provenance:

- Add `JobTriggerKind::App`; `execute_component` stamps every app-launched job with
  `trigger_kind = 'app'` + `trigger = <app path>`. A direct `/jobs/run` cannot set
  this, so it is the authoritative signal that a file was produced *by the app*.
- The provenance gate's recent-production check collapses to
  `trigger_kind = 'app' AND trigger = <this app path>` (+ the 3h window and result
  containment). This drops the forgeable `created_by`/`permissioned_as`/
  `runnable_path`/kind logic entirely and removes the `LIKE` wildcard issue.
- Provenance is scoped to THIS app's path, so another app's jobs (even same author)
  do not authorize this app's reads.

Regression test rewritten to the marker model: an app-produced key clears for viewer
and admin; a direct run whose `permissioned_as` resolves to the author stays denied
(the forgery); another app's output stays denied. Adds `app` to the OpenAPI
JobTriggerKind enum.

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

* test(apps): assert execute_component stamps trigger_kind='app' at runtime

Adds an end-to-end test that runs a real script component through the app
runtime (`apps_u/execute_component`) and asserts the enqueued job carries the
app-origination marker `trigger_kind = 'app'` + `trigger = <app path>` (not the
runnable path). The provenance-gate tests seed the marker directly; this proves
the runtime actually produces the exact marker the gate depends on.

execute_component commits the job row and returns its id, so the assertion reads
the row directly — no worker needed to run the job.

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

* fix(triggers): reject trigger_kind=app for suspended-job reassignment (review)

`JobTriggerKind::App` (added for the app-origination S3 marker) became a valid
value for the resume/cancel suspended-trigger routes, whose handler derives the
table name `<kind>_trigger`. There is no `app_trigger` table, so both endpoints
would fail with a missing-relation database error (500). Reject `App` in
`get_suspended_trigger` alongside webhook/schedule so it returns a clean 400.

Adds a regression test asserting the reassignment route returns 400 (not 500) for
trigger_kind=app.

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

* fix(apps): don't stamp app-origination marker on preview runs (review)

The app-origination marker (trigger_kind='app') was stamped unconditionally,
including preview mode. A preview lets a `jobs:run` caller supply arbitrary
`raw_code` against ANY app path without that app's deployed policy (raw_code with
no path/id skips all app authorization), so a preview returning
`{"s3":"<author-only-key>"}` would forge the exact marker the S3 provenance gate
trusts and read the victim app author's file.

Gate the marker on `!is_preview`: only deployed, policy-checked executions are
app-provenanced. Preview/editor S3 display does not rely on this marker (the editor
routes reads through the force_viewer allowlist), so nothing legitimate regresses.

Adds a regression test asserting a preview run's job is not stamped trigger_kind='app'.

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

* fix(apps): editor-authorize preview marker + per-viewer S3 provenance isolation (review)

Closes the codex P1 (preview forgery) without breaking editor preview downloads,
and adds cross-viewer isolation to the provenance gate.

- Preview marker now requires app write: `execute_component` stamps the
  app-origination marker on a preview only when the caller can EDIT that app
  (`require_is_writer`), instead of never stamping previews. An app editor already
  wields the app's author identity (they can deploy a component that reads the same
  file), so marking their own preview is no escalation and keeps preview-produced
  S3 results downloadable in the editor; a `jobs:run`-only caller who cannot edit
  the app still cannot forge the marker. Deployed runs are unchanged (always
  marked).

- Per-viewer isolation: the provenance gate now also requires
  `j.created_by = <this caller>`. The security boundary stays the un-forgeable
  `trigger_kind='app'` marker; `created_by` is an additional filter ANDed under it,
  so it only narrows — a viewer can only download keys their OWN app runs produced,
  not another viewer's result. Restores the per-caller scoping #10048 had, now safe
  on top of the marker.

Tests: preview marked iff caller can edit the app; cross-viewer isolation (another
viewer's app-marked key denied, no admin bypass); direct-run and other-app keys
still denied; deployed run still stamped.

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

* fix(apps): require apps:write scope (not just writer ACL) to mark preview provenance (review)

require_is_writer checks the user's underlying ACL but ignores token scopes, so a
writer's token deliberately scoped to apps:run/apps:read/jobs:run but WITHOUT
apps:write could still mark a preview and forge provenance — even though that token
cannot deploy the app (update_app requires apps:write), breaking the "any marked
caller can deploy equivalent code" rationale.

Require BOTH apps:write:<path> scope (check_scopes) AND the writer ACL
(require_is_writer) before stamping a preview's app-origination marker. Deployed
runs unchanged.

Adds a scope-restricted-writer token to the test (apps:run/read + jobs:run, no
apps:write) and asserts its preview stays unmarked; retains the full-editor
positive case and the non-editor negative case.

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

* fix(apps): never app-provenance preview runs; read editor S3 as the caller (review)

Simplifies the preview handling: a preview executes as the *caller* (Viewer mode),
never as the author, so its results must be read back as the caller — never
author-mode — and must never carry the app-origination marker. This removes the
whole `require_is_writer` / `apps:write` / `can_preserve_on_behalf_of` reasoning
(which was also unsound: a writer's token or session may not be able to deploy a
component running as the app's on-behalf identity, so marking their preview could
still escalate).

- Backend: mark the app-origination marker for deployed runs only (`!is_preview`).
- Frontend: `getS3File` (AppImage/AppPdf/AppDownload) now routes editor/preview
  reads through the viewer-scoped `job_helpers/download_s3_file` endpoint (reads as
  the caller), matching what DisplayResult/ParqetCsvTableRenderer already do; only
  a deployed app view uses the provenance-gated `apps_u` endpoint. This is the path
  that previously relied on marking previews, so nothing regresses.

Test: a preview is never app-provenanced (owner's own preview and a non-editor's
both stay unmarked). Cross-viewer isolation, deployed marking, and the reassignment
guard are unchanged.

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

* fix(apps): app components run on-behalf of the app, not the referenced runnable (review)

Root-causes codex's on-behalf-preview finding: `execute_component` was overriding the
app's resolved on-behalf identity with the referenced script/flow's OWN
`on_behalf_of` (its `on_behalf_of_email`). That is wrong in the app context — the
app's execution mode should govern:

- A Viewer-mode app could execute a component AS the referenced runnable's on_behalf
  identity (privilege confusion / escalation), instead of as the viewer.
- A preview would run as that identity rather than as the caller, so its S3 output
  could not be read back as the caller — the download-identity mismatch codex flagged.

Always use the app-resolved identity (author in author-mode, caller in
viewer/preview); a referenced runnable's own `on_behalf_of` no longer leaks into app
execution. Direct `/jobs/run` still honors a runnable's `on_behalf_of` (unchanged).
With this, previews always run as the caller, so reading editor/preview S3 as the
caller (viewer-scoped `job_helpers`) is unconditionally correct.

- Test: the deployed-component e2e now seeds the script with a distinct on_behalf and
  asserts the component job's `permissioned_as` is the app identity, not the script's.
- Also reword the getS3File `configuration` param comment to describe current state
  only (AGENTS.md comment rule).

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

* chore(apps): surface 'app' trigger kind in Runs UI; condense provenance comments (review)

Addresses codex review nits:
- Add `app` to `jobTriggerKinds`, `triggerIconMap` (LayoutDashboard), and
  `triggerDisplayNamesMap` so app-component jobs (which now carry
  `trigger_kind = 'app'`) are filterable in Runs and render their trigger info.
- Condense the app-origination marker, on-behalf-identity, and provenance-gate
  comments to state each invariant once in <=4 lines at its relevant site
  (AGENTS.md comment rule).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:29:48 +02:00
Ruben Fiszel 22b47c8823 chore(main): release 1.756.0 (#10062)
* chore(main): release 1.756.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-12 10:30:58 +02:00
Ruben Fiszel 1ed7fc066b chore(main): release 1.755.0 (#10041)
* chore(main): release 1.755.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-12 00:01:39 +02:00
Ruben Fiszel ff774c46bf feat: add per-workspace job-retention override (#10050)
* feat: add per-workspace job-retention override (EE)

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

* chore: update ee-repo-ref to 2ba6a2a75b6fc97858b306b2c98ada481e363c10

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

Previous ee-repo-ref: e7fb36acd813cd717bcf05f5aafbf81de271d618

New ee-repo-ref: 2ba6a2a75b6fc97858b306b2c98ada481e363c10

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-11 23:46:34 +02:00
Ruben Fiszel 04eb7ddd39 fix: clearer errors on auto-draft save failure (WIN-2157) (#10053)
* fix: clearer errors on auto-draft save failure (WIN-2157)

When an autosave draft save fails, the cloud indicator now surfaces the
backend reason on hover (native title tooltip) in addition to the
existing click popover, so the cause is discoverable without a click.

Backend now returns a clearer, actionable message:
- `require_can_write_path` distinguishes a malformed path (unrecognized
  namespace prefix -> BadRequest) from a genuine permission denial, and
  the deny message spells out where the user *can* write.
- `require_owner_of_path` no longer panics with an out-of-bounds index on
  a malformed single-segment path (e.g. a bare `u`/`f`); it returns a
  clear BadRequest instead. Covered by a regression test.

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

* chore: trim narrative comment to invariant in drafts.rs (WIN-2157)

Address CI review (AGENTS.md: comments record constraints, not narration,
≤4 lines): keep the malformed-path invariant, drop the motivation tail.

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

* fix: don't let a malformed stored draft 400 the draft listing (WIN-2157)

Address CI review (P1): require_can_write_path can now return BadRequest
for a malformed path, and list_drafts propagated it — so a single
malformed stored draft row (the draft table has no path constraint;
legacy/admin-authored rows may be malformed) would make GET /drafts/list
return 400. Treat BadRequest like NotAuthorized there: the row is simply
not writable. Verified e2e on EE — listing returns 200 with can_write
false for the malformed rows.

Also trim "unchanged"/"still" drafting-history narration from the
regression test comments (P2, AGENTS.md).

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

* chore: compress list_drafts comment to 4 lines (WIN-2157)

Address CI review P2: keep the constraint (draft table has no path
constraint) and the invariant (one malformed row must not 400 the
listing) within the AGENTS.md ≤4-line limit.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 16:00:53 +02:00
Ruben Fiszel 1e192f2d86 feat(apps): authorize deployed-app S3 reads on-behalf of the author for logged-in viewers (#10048)
* feat(apps): authorize deployed-app S3 reads on-behalf of the author for logged-in viewers

A logged-in user viewing a deployed app now reads S3 files (rich result,
table/image/PDF preview, CSV export, download, metadata) the same way an
anonymous viewer already does: on-behalf of the app author per the app policy's
execution_mode, gated by an app-provenance check — instead of against the
viewer's own S3 permissions. This aligns S3 with every other thing an app does
(scripts, flows, resources all already run on-behalf of the author) and lets an
operator who lacks folder S3 permission still see data rendered inside the app.

The raw job_helpers/* S3 API stays viewer-scoped: a viewer who lacks folder
permission is still denied there. Only which endpoint the app frontend uses for
logged-in deployed viewers changes.

Backend:
- Add app-scoped, provenance-gated apps_u/* variants for all S3 display ops
  (download_s3_file already existed; add download_s3_parquet_file_as_csv,
  load_file_metadata, load_file_preview, load_parquet_preview, load_csv_preview,
  load_table_count). Each routes through one shared helper
  (app_s3_on_behalf_and_provenance) that scope-confines an app embed token,
  resolves the on-behalf identity, and runs the provenance gate ONCE before
  dispatching to the EE *_internal S3 helpers.
- Close the confused-deputy hole in check_if_allowed_to_access_s3_file_from_app:
  the unconditional Ok() bypass for a logged-in, non-embed session now only
  applies in viewer execution mode (where the on-behalf identity IS the viewer,
  so the viewer's own permissions still bound the read downstream). Author-mode
  reads (anonymous/publisher) always enforce provenance, for anonymous and
  logged-in viewers alike, so a viewer cannot launder the author's S3
  permissions with an arbitrary file_key.

Frontend:
- Route the deployed-app view through apps_u/* using the app-viewer isEditor
  signal instead of login state (the old $userStore proxy wrongly sent
  logged-in deployed viewers to the viewer-scoped job_helpers API). Editor and
  preview keep viewer identity via job_helpers.

execution_mode: viewer remains the escape hatch for per-viewer S3 enforcement.

Fixes provenance-gated S3 display for logged-in operators on deployed apps.

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

* docs(backend): document cargo features, restarting the dev backend, and filesystem object storage

The dev backend runs `cargo watch --features quickjs` by default, which omits S3,
EE, MCP, and non-JS runtimes — feature-gated routes then 404 or return a
"requires <feature>" stub at runtime. Add a backend/CLAUDE.md section that:

- explains that you must restart the backend with the appropriate features to
  exercise gated functionality, with the pid/cwd-scoped restart recipe (never
  pkill target/debug/windmill) and the PORT=$BACKEND_PORT gotcha;
- documents what each commonly-toggled feature gate does (private, enterprise,
  license, parquet, duckdb, language runtimes, mcp, trigger kinds, no_auth) plus
  common combinations;
- documents using the built-in FilesystemStorage large-file storage for dev
  workspace object storage (hidden from the UI dropdown; set via
  edit_large_file_storage_config), including the advanced_permissions shape.

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

* fix(apps): don't flatten inner query in app-scoped S3 preview routes

axum's `Query` uses `serde_urlencoded`, which cannot deserialize the typed
(numeric/bool) fields of a `#[serde(flatten)]`-ed struct and 400s on `limit` /
`offset` ("invalid type: string, expected u32"). The app-scoped
load_csv_preview / load_parquet_preview / load_table_count routes flattened
LoadPreviewQuery / LoadCountQuery, so their previews were broken. Restate the
fields directly on the outer query structs (with an into_inner() to rebuild the
inner query) and extend the CE OSS stub to match.

Also bumps ee-repo-ref.txt for the companion EE csv-separator panic fix.

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

* fix: address CI review — nested DisplayResult routing, byte-range contract, docs, tests

- [P1] Thread `appPath` into the nested `DisplayResult`s (render_all children and
  the expanded-result drawer) so logged-in deployed viewers route nested/expanded
  S3 tables, images, PDFs, and downloads through `apps_u/*` too, not job_helpers.
- [P2] Mark `read_bytes_from`/`read_bytes_length` required on the
  `apps_u/load_file_preview` route (they are non-optional in LoadFilePreviewQuery),
  and mirror the full query shape in the CE OSS stub so the byte-range contract is
  enforced identically on CE and EE.
- [P2] Fix the backend retrigger command in backend/CLAUDE.md: cargo watch runs
  from `backend/`, so `touch README.md` (not `backend/README.md`).
- [P2] Trim app_s3_onbehalf.rs comments per AGENTS.md (state the invariant once,
  no drafting-history narration).
- Extend the integration test to cover the table-count, csv-preview (numeric
  limit/offset deserialization), and file-preview (byte-range required) routes.

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

* docs(apps): tighten S3 provenance-gate comments per AGENTS.md

Consolidate the viewer-mode / author-mode rationale to ≤4 lines at each branch
of the gate, and drop the repeated explanation from the shared
app_s3_on_behalf_and_provenance doc comment (which now just states what the
helper does). No behavior change.

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

* chore: update ee-repo-ref to f292a1040da6a667ce7c22abf63ec0debfdd480f

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

Previous ee-repo-ref: a582389084eb363997cb5e8053f29220e0d3eaec

New ee-repo-ref: f292a1040da6a667ce7c22abf63ec0debfdd480f

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-11 10:14:40 +02:00
Ruben Fiszel 5844c32ac5 fix: enforce read authorization when signing S3 objects (#10049)
`sign_s3_objects` minted a long-lived HMAC bearer signature for any S3 key
handed to it, by any authenticated workspace member, with no check that the
caller was allowed to read that key. Since `validate_s3_signature` only
verifies the HMAC and expiry at fetch time, any member (operators included)
could mint a transferable capability to read arbitrary S3 keys, bypassing the
advanced S3 permission rules (`check_lfs_object_path_permissions`).

Authorize the read at mint time: add an `ApiAuthed` extractor and, before
signing each key, require the caller's own `S3Permission::READ` via
`get_workspace_s3_resource_and_check_paths`. A caller can no longer sign a key
they cannot themselves read. The fetch-side validators are left unchanged.

The only legitimate caller is the wmill SDK invoked from an app-author job,
whose token authenticates as the executing (author) identity — which can read
the key — so authorized app display is unaffected.

Adds an integration test proving an authorized caller can sign a readable key
(and the signature validates end-to-end through the presigned fetch route)
while an unauthorized caller is refused.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 05:57:48 +02:00
Ruben Fiszel 8343203ec2 feat(mcp): add multi-workspace MCP tokens via the gateway endpoint (#10043)
* feat(mcp): add multi-workspace MCP tokens via the gateway endpoint

A single MCP token with no bound workspace (workspace_id NULL + mcp scope)
now works across every workspace the token owner can access, served through
the existing /api/mcp/gateway endpoint. This avoids having to register one
MCP server entry per workspace in clients like Claude/Cursor.

In multi-workspace mode the runner exposes a synthetic `list_workspaces`
tool plus the generic API endpoint tools, each workspace-scoped one gaining
a required `workspace_id` argument (mirroring the proxy pattern users built
externally). Per-workspace scripts/flows are not enumerated to avoid
flooding the tool list — they are run via runScriptByPath/runFlowByPath
with an explicit workspace_id.

Auth is resolved per tool call: the gateway middleware detects a
workspace-less mcp token and marks the request MultiWorkspaceMcp, and the
runner resolves a per-workspace ApiAuthed from the raw token via the
AuthCache (validating membership; superadmins may act in any workspace).
Single-workspace tokens are unchanged.

Frontend: the MCP token creation flow gains an "All workspaces" option that
produces a workspace-less token and the gateway URL.

Fixes WIN-2153

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

* test(mcp): cover multi-workspace endpoint tool transformation

Unit tests for endpoint_tool_to_mcp_tool_multi and list_workspaces_tool:
workspace-scoped tools gain a required workspace_id arg, global tools are
left unchanged, workspace_id is not duplicated, and list_workspaces takes
no arguments.

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

* fix(mcp): forward script/flow args for runScriptByPath/runFlowByPath

These endpoints have an additionalProperties body (no declared properties),
so build_request_body previously returned an empty body and dropped every
script/flow argument. This was latent for the per-path run endpoints and
became load-bearing in multi-workspace mode, where scripts/flows can only be
run via runScriptByPath/runFlowByPath — parameterized runs silently lost
their arguments.

build_request_body now forwards all arguments not consumed by a path/query
parameter for pass-through (additionalProperties) bodies, keeping the strict
declared-only behavior for endpoints with explicit properties. The runner
strips the synthetic workspace_id argument before dispatch so it can't leak
into the forwarded body.

Reported by Codex review on #10043.

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

* feat(mcp): note workspace_id requirement in multi-workspace tool descriptions

Workspace-scoped tools already gain a required workspace_id parameter (with
its own schema description) in multi-workspace mode, but the tool's prose
description was unchanged. Append a note so models/clients that read the
description text know to pass workspace_id (and to call list_workspaces
first). Global tool descriptions are left untouched.

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

* refactor(mcp): trim multi-workspace tool/arg descriptions

The workspace_id note repeats across every workspace-scoped tool in each
tools/list, so keep it terse: description suffix "Requires `workspace_id`."
and arg description "Target workspace id (from list_workspaces)." to avoid
spending tokens on repeated boilerplate.

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

* fix(mcp): enforce script/flow path scopes for multi-workspace run-by-path

In multi-workspace mode runScriptByPath/runFlowByPath are the only way to run
scripts/flows, but they were authorized against the endpoint scope only — never
the caller's mcp:scripts:/mcp:flows: path scopes. A granular token could run
items outside its allowed paths (e.g. mcp:scripts:f/team/* + mcp:endpoints:*
running f/other/secret), and a mcp:endpoints:* token could run arbitrary
scripts.

Now these two endpoints are authorized by the script/flow scope of the
requested path (matching single-workspace mode's per-item tools): exposed in
list_tools only when the token grants some script/flow (McpScopeConfig::has_any),
and at call time the path is checked via is_allowed("script"/"flow", path).

Verified e2e: mcp:scripts:f/team/* runs f/team/* but is denied f/other/*;
mcp:endpoints:* alone no longer exposes or runs run-by-path.

Reported by Codex + Pi review on #10043.

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

* fix(mcp): deny run-by-path for mcp:favorites multi-workspace tokens

mcp:favorites sets granular=false, so the previous run-by-path scope check
(gated on `granular`) was skipped entirely — a default "Favorites only"
all-workspaces token could run any script/flow by naming its path, bypassing
the favorites restriction.

Favorites are an enumerated set reachable only through per-item tools, not by
arbitrary path, so they grant nothing for run-by-path. has_any() now returns
true only for mcp:all (not favorites), and the call-time check drops the
`granular` gate and relies on is_allowed() directly (already false for
favorites, true for mcp:all, pattern-matched for granular).

Verified e2e: mcp:favorites no longer exposes or runs run-by-path; mcp:all
still runs; granular script scopes still path-enforced.

Reported by Codex review on #10043.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:32:30 +02:00
Ruben Fiszel 38a190b53a chore(main): release 1.754.0 (#10017)
* chore(main): release 1.754.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-10 13:59:22 +00:00
Ruben Fiszel 9feda57c15 perf: index v2_job(parent_job) to speed up run child-job listing (#10034)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 07:51:16 +00:00
Ruben Fiszel 9ad6927231 chore(main): release 1.753.0 (#9997)
* chore(main): release 1.753.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-08 16:46:15 +00:00
Ruben Fiszel f28ea9cb99 feat(db-health): add connection sizing guidance (#10014)
* feat(db-health): add connection sizing guidance

The Database Connections panel showed current/max connections but gave no
guidance on how to size max_connections for the deployment. Derive an estimate
from the live worker fleet: each worker instance shares a pool sized
DEFAULT_MAX_CONNECTIONS_WORKER + (workers - 1), and each server opens up to
DEFAULT_MAX_CONNECTIONS_SERVER (both overridable via DATABASE_CONNECTIONS).

The endpoint now returns live worker/instance counts, the default per-server
and per-worker pool sizes, the estimated peak worker connections, the reserved
superuser connections, and a recommended max_connections floor (workers + one
server + 25% headroom). Servers do not ping worker_ping, so the recommendation
assumes one server and exposes the per-server increment. The panel renders this
as a sizing breakdown and warns when max_connections is below the recommended
floor.

Fixes WIN-2147

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

* refactor(db-health): single source for pool-size constants + sizing tests

Address review: db_connect.rs kept its own copies of DEFAULT_MAX_CONNECTIONS_*
that duplicate the windmill_common constants the sizing guidance reads, so
tuning the runtime pool size would silently leave the guidance stale. Re-export
the windmill_common constants from db_connect.rs so there is one source of truth.

Add unit tests for compute_connection_sizing covering the zero-fleet, single
worker, multi-instance, and reserved-clamp cases.

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

* feat(db-health): 20% headroom and 200-connection minimum floor

Lower the sizing headroom from 25% to 20% and never recommend below 200
connections (postgres defaults to 100; cheap headroom for growth/bursts/psql).
Update the guidance message and unit tests accordingly.

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

* fix(db-health): honor DATABASE_CONNECTIONS in sizing recommendation

Address Codex P1: the runtime caps every process's pool at DATABASE_CONNECTIONS
when set (db_connect.rs), but the sizing guidance always used the default 50/5
pools. For a tuned deployment this under-estimated worker demand and could hide
a genuine under-provisioning (e.g. DATABASE_CONNECTIONS=100 with 5 instances is
500 worker connections, not 25).

compute_connection_sizing now takes the effective DATABASE_CONNECTIONS override
(read the same way db_connect.rs reads it): when set, each worker instance and
server pool is that value and the worker estimate is override * instances. The
response exposes server_pool_size / worker_pool_size (effective) and
database_connections_override; the panel renders both pool rows and labels them
(default) vs (DATABASE_CONNECTIONS), and the message states which source is used.
Adds a unit test for the override path.

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

* fix(db-health): exclude agent workers from connection sizing

Agent workers reach the API over HTTP (MODE=agent, Connection::Http) and hold
no postgres pool, but their pings still land in worker_ping (written server-side
by /api/agent_workers/update_ping). Counting them inflated the connection
estimate. Filter the fleet query by the worker-name prefixes: DB-connected
workers use "wk-" (WORKER_NAME_PREFIX), agent workers use "ag-"
(AGENT_WORKER_NAME_PREFIX). Only wk- workers/instances feed the estimate; ag-
workers are counted separately and surfaced as context ("N agent workers
excluded — they use HTTP, not postgres connections"). Adds a unit test.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 16:42:39 +00:00
Ruben Fiszel 223e1569ce chore(main): release 1.752.0 (#9974)
* chore(main): release 1.752.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-07 22:10:10 +02:00
Diego Imbert e47aedac0a feat: add SQL migrations for data tables (#9693)
* feat: add datatable_migrations table

* feat: add route to run datatable migrations

* feat: sync datatable migrations as .up.sql/.down.sql files

* feat: add datatable migrate up/down commands and post-push run prompt

* feat: add datatable migrate new command to scaffold migrations

* feat: add datatable migrations management UI

* feat: prompt to create migration on DDL in datatable SQL editors

* feat: support running a single specific datatable migration

* feat: view migration content, run single migration, fix stacked modal

* feat: per-row revert button with out-of-order warning

* fix: avoid migrations list flicker on refresh after an action

* feat: generate initial datatable migration via pg_dump

* fix: surface datatable migration API error details in toasts

* fix: revert created migration if create-and-run fails to run

* fix: include postgres error detail in migration run/rollback failures

* feat: sync datatable migrations as files via the workspace export

* refactor: move datatable migrations to migrations/datatable/ path

* fix: drop redundant datatable_migration label in sync output

* fix: exclude datatable migration sql files from script metadata generation

* feat: run datatable migrations as user-permissioned labeled jobs

* feat: reject invalid datatable migrations on sync push

* feat: datatable migrate up/down default to all datatables, --datatable to target one

* fix: surface postgres error detail when datatable migrations fail to run

* chore: regenerate CLI docs for datatable migrate commands

* feat: default new datatable migration to a BEGIN/END transaction template

* fix: validate datatable migration name and datatable at the API boundary

* fix: ensure detected DDL ends with semicolon when wrapped in transaction

* fix: re-prompt instead of stripping DDL when new-migration modal is cancelled

* feat: refresh datatable schema after running a migration from the SQL REPL

* feat: record db manager DDL on data tables as migrations

* feat: make datatable migrations opt-in per data table

* fix: make migration view editor read-only so its code can scroll

* fix: don't re-prompt DDL guard when creating a migration without running

* feat: generate down migrations for db manager DDL (postgres)

* fix: correct down migration for db manager alters (no double-wrap, serial)

* feat: explain migrations purpose with a tooltip in the migrations modal

* compare paeg

* feat: add datatable_migration kind to workspace diff pipeline

* chore: point ee-repo-ref at datatable_migration git-sync companion

* fix: harden datatable migration version allocation and initial-migration bookkeeping, add tests

* feat: deploy and run datatable migrations on workspace merge

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

* Refactor + handle datatable setting delete/rename

* refactor: move datatable migration rename/delete cascade into module

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

* chore(windmill-utils-internal): bump to 1.7.1 for datatable migration deploy provider methods

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

* feat(db-manager): add Migrations button to top bar, make Refresh icon-only

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

* BEGIN/END placeholder in down migration

* feat: autofocus migration name input and flag it red when empty

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

* feat(datatable-migrations): allow non-admins to create/run/revert migrations, gate only opt in/out

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

* border nits

* refresh db manager schema on migrations

* BEGIN/END scaffold in CLI

* feat(cli): push local datatable migrations before running on migrate up

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

* feat: flag invalid migration name with red border, not just empty

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

* refactor: drop random slug from auto-generated migration names

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

* feat: offer revert-and-delete when deleting an installed migration

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

* feat: record fork merge as a migration when target datatable opts in

* nit

* clone migrations on fork

* windmill-utils-internal

* fix(datatable-migrations): serialize run/rollback with a per-db advisory lock

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

* fix(db-manager): fail closed when migrations-status check errors on DDL apply

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

* docs: fix generate_initial migration ordering comment to match code

* chore(datatable-migrations): remove unused update_datatable_migrations endpoint

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

* fix: run DDL migration guard on the script editor Test button

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

* split

* ee-repo-ref

* chore(frontend): sync package-lock with package.json (@emnapi deps)

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

* fix(datatable-migrations): never resolve instance credentials into migration job args

datatable_database_arg eagerly resolved instance data-table credentials
(including the shared instance-wide Postgres password) and passed them as the
migration job's plaintext `database` arg, landing in v2_job.args. Since the
run route has no admin gate, a non-admin could run a migration and read
args.database to recover the password, granting cross-workspace psql access to
all instance data-table DBs.

Pass a `datatable://<name>` reference for both resource-backed and instance
data tables instead; the pg executor already resolves it to real credentials
server-side at run time, so nothing sensitive is ever stored in the job args.

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

* nit

* fix: handle dollar-quoting and comments when splitting SQL statements

* feat: deploy datatable migrations on merge with explicit opt-in error

* fix(frontend): sync package-lock with npm 11 peer-dep resolution

npm ci failed with 'Missing: @emnapi/core@1.11.2 / @emnapi/runtime@1.11.2 from
lock file'. @napi-rs/wasm-runtime declares @emnapi/core|runtime ^1.7.1 as
peerDependencies while @rolldown/binding-wasm32-wasi pins them to exactly
1.10.0. Newer npm (bundled with node 24 in CI) installs the peer deps at the
highest match (1.11.2) alongside rolldown's nested 1.10.0, so the ideal tree
needs both versions; the committed lock only had 1.10.0.

Regenerate the lock with npm 11.18 so it carries both 1.11.2 (top-level, for
the peer deps) and 1.10.0 (nested, for rolldown's pin). Verified npm ci passes.

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

* nit npm publish

* fix: fail closed on migrations-status error in fork schema merge

* nit CI emnapi/core version

* prevent initial_datatable_migration if migrations already exist

* fix(datatable-migrations): validate persisted data table names as path segments

edit_datatable_config only validated rename segments, not the actual
settings.datatables keys, so a data table could be saved directly under a name
like '..' or one containing '/'. Since new tables default to
migrations_enabled = true, generate_initial_datatable_migration would then
insert a migration row and the sync export would build
migrations/datatable/<name>/... paths from that name, producing malformed or
directory-escaping export paths.

Validate every persisted data table name in edit_datatable_config (alongside
the existing rename checks) and add validate_datatable_path_segment to
generate_initial_datatable_migration for defense in depth.

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

* fix: scope datatable _wm_migrations by data table and cascade renames/deletes

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

* fix(system_prompts): resolve nested local command groups in CLI docs generator

The CLI docs generator anchored on the first `new Command()` in a file and
never resolved locally-defined command groups passed as
`.command("name", localCmd)`. For datatable this flattened the nested
`migrate` group: it emitted `datatable new/up/down` plus a bare
`datatable migrate`, and mislabeled the datatable command with the migrate
group's description. jobs was broken the same way (its description was pull's,
and pull/push rendered empty).

Anchor block extraction on the `export default`ed command, recurse into
locally-defined `const x = new Command()` groups mounted as subcommands, and
render nested sub-subcommands. Regenerated docs now show
`datatable migrate new/up/down` and `jobs pull/push` with their real
options.

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

* refactor: drop unreleased _wm_migrations legacy-upgrade handling

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

* fix: return datatable migration SQL from getItemValue for the diff drawer

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

* chore(frontend): use windmill-utils-internal 1.8.2 for migration diff drawer

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

* nit

* nit

* fix: handle datatable migration renames on push and dedupe timestamps

* fix: reject rewriting an already-applied datatable migration on upsert

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

* fix(frontend): add missing @emnapi/core and @emnapi/runtime lockfile entries

Resolves npm ci EUSAGE failure: the optional cpu:wasm32 @rolldown/binding-wasm32-wasi
declares deps on @emnapi/core@1.11.2 and @emnapi/runtime@1.11.2 that had no resolved
lockfile entries.

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

* fix(cli): datatable migrate up/down default to main datatable, not all

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

* fix: fail closed when applied status unreadable on datatable migration rewrite

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

* fix: surface full error detail in Database Manager DDL/query errors

* "See migration" button in the toast

* feat: add Enter shortcut to Create-a-migration in the DDL guard

* fix(frontend): warn before running a newly-created datatable migration out of order

The row-level Run action warns when earlier migrations are still pending, but
the create-and-run paths ran a just-created migration with `only` directly,
applying it ahead of older pending migrations without that confirmation.

Reuse the same "Run migration out of order" confirmation across all
create-and-run paths via a shared helper (datatableMigrationUtils):
- NewDataTableMigrationModal "Create and run" (and the DDL guard path)
- DatatableSchemaDiff fork→parent merge
- dbOps schema ops (DB manager create/alter/drop) — the pure factory throws a
  MigrationRunCancelled sentinel on decline, which DBTableEditor treats as a
  silent cancel

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

* fix: keep renamed datatable migrations visible in compare view

* fix: record per-migration deployment on datatable migrations disable

* fix(cli): run deployed datatable migrations after workspace merge

The merge command upserted datatable_migration definitions into the target
workspace and reported the item as successfully deployed, but never ran the
migrations. For forked datatables backed by separate databases, this left the
target schema unchanged until someone manually ran `wmill datatable migrate up`,
while the CLI reported a successful merge.

Collect the datatable migrations deployed (not deleted) into the target and,
after the deploy loop, offer to run them via the existing offerToRunNewMigrations
helper — the same post-deploy run prompt the push/sync path uses (interactive
only; `--yes`/non-TTY skip the mutating run, matching push behavior). Export
parseDatatableMigrationDeployPath so the merge path can parse the deployed items.

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

* fix(backend): serialize datatable migration edits/deletes with the run lock

A migration run snapshots a migration's code_up from datatable_migrations and
only records its version in the data table's _wm_migrations after the job
succeeds. upsert_datatable_migration checked _wm_migrations before allowing an
edit but took no lock, so a concurrent edit could read "not applied yet",
rewrite code_up/code_down, and then the in-flight run would record the version
for the old SQL — leaving _wm_migrations pointing at SQL that was never applied
(migrate up then skips it; rollback runs a down that doesn't match).

Serialize definition rewrites and deletes with the same per-database advisory
lock the run/rollback paths use:
- Factor the connect+advisory-lock into lock_datatable_migration_runs and the
  applied-versions read into read_applied_versions_on_client.
- run_datatable_migrations now snapshots the definitions AFTER taking the lock,
  so code_up can't change between snapshot and version-record.
- upsert (when changing an existing def) and delete take the lock across the
  applied-check and the write; delete now rejects deleting an already-applied
  migration (would orphan its _wm_migrations record), symmetric with upsert.
  Both fail closed if the data table database is unreachable.

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

* fix(frontend): stack the out-of-order migration confirm above the DB editor preview

Creating a table on a migrations-enabled data table opened the DB table editor's
"Confirm running the following" preview modal, whose confirm triggers applyDdl,
which then asks for out-of-order confirmation. Both are ConfirmationModals with a
hardcoded z-[9999]; the out-of-order one lives in DBManagerContent (mounted before
the editor), so it rendered behind the still-open preview modal.

Add an optional zIndexClass prop to ConfirmationModal (default z-[9999],
backward-compatible) and give the DB-manager out-of-order confirm z-[10000] so it
stacks on top.

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

* chore: update ee-repo-ref to 27672e37df5d9dfde94f19963d5ffcdf8dd5448c

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

Previous ee-repo-ref: 6c287041cd7edd4a77a4bc07ad0e156cec32cce4

New ee-repo-ref: 27672e37df5d9dfde94f19963d5ffcdf8dd5448c

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-07 08:25:16 +00:00
Ruben Fiszel 8dd5e48a68 chore(main): release 1.751.0 (#9965)
* chore(main): release 1.751.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-06 19:00:48 +00:00
Ruben Fiszel 97d14d979f bun bootstrap housekeeping on the migrator's held connection (#9970)
migrate() and fix_flow_versioning_migration re-acquired a second connection from the pool while already holding one (the migrator's checked-out, advisory-locked connection). That deadlocks any backend limited to one connection at a time — connection-constrained managed Postgres, PgBouncer transaction pooling, or an embedded single-connection dev database. Route those housekeeping queries onto the already-held connection via a new CustomMigrator::connection() accessor. Fewer connections during migration and, for fix_flow_versioning, the existence check and write now run on the same advisory-locked connection. Default multi-connection behavior is unchanged.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 18:59:25 +00:00
hugocasa fd8e64d11f feat: add cosmetic dev/staging label for dev workspaces (#9959)
* feat: add cosmetic dev/staging label for dev workspaces

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

* feat: prefill dev fork name and use a link to switch its label

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

* style: reword the dev/staging label link copy

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

* style: preview the dev/staging label as a badge in the switch link

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

* feat: show the dev/staging badge in the session diff drawer header

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 18:43:17 +00:00
Ruben Fiszel 5fe7e1f3e8 chore(main): release 1.750.0 (#9952)
* chore(main): release 1.750.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-06 11:39:57 +02:00
windmill-internal-app[bot] aaeb9524b3 chore: refresh vendored docs snapshot (#9955)
Co-authored-by: hugocasa <15649739+hugocasa@users.noreply.github.com>
2026-07-06 10:01:13 +02:00
Ruben Fiszel 891b32195a chore(main): release 1.749.0 (#9938)
* chore(main): release 1.749.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-06 01:40:42 +02:00
Ruben Fiszel 46be39dfb7 fix(pipelines): order data_test relationships refs before the tested script in a cascade (#9934)
* fix(pipelines): order data_test relationships refs before the tested script in a cascade

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

* fix(pipelines): key custom-test reads by (usage_kind, path) to avoid same-path flow collisions

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 22:34:58 +02:00
Ruben Fiszel 799b9e3b7c chore(main): release 1.748.0 (#9914)
* chore(main): release 1.748.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-05 18:23:51 +02:00
Ruben Fiszel 39eb9de1bc feat(pipelines): fork data environments for ducklake materialization (dev data) (#9915)
* feat(pipelines): fork-scoped ducklake namespaces with read-defer to parent

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

* feat(pipelines): fork graph indicator + fork ducklake namespace cleanup endpoint

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

* feat(pipelines): fork_views-keyed view transition, fork lineage clone, design doc

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

* fix(pipelines): review hardening - fork DATA_PATH last-wins, registry cache TTL, defer tests

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

* feat(pipelines): per-lake isolated/shared choice at fork creation

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

* fix(pipelines): chain-aware defer discovery + per-location fork namespace registry

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

* fix(pipelines): lake-scoped fork schemas, catalog identity in registry, chain-aware graph chips

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

* fix(pipelines): cleanup deletes fork data from the registered storage identity

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

* fix(pipelines): collapse fork data-path segment to one component (slash-safe ids)

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

* fix(pipelines): per-catalog ancestor checks, ancestor extra_args passthrough, test compile fix

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

* fix(pipelines): invalidate fork ancestor-chain cache on lineage mutations

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

* fix(pipelines): sweep descendant ancestor-chain caches on delete/reparent

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

* fix(pipelines): run fork ducklake cleanup inline in delete_workspace

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

* fix(pipelines): resolve fork cleanup credentials pre-commit, destroy post-commit

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

* fix(pipelines): shared dev-workspace authz gate for namespace drop, invalidatable registration cache, segment-boundary delete filter

- extract require_prod_admin_for_dev_workspace, used by both delete_workspace
  and drop_forked_ducklake_namespaces so the gates cannot drift
- key FORK_DUCKLAKE_REGISTERED per workspace and invalidate it in
  cleanup_fork_ducklake_namespaces so a same-id fork recreated within the TTL
  re-registers its namespaces
- filter listed object locations to the segment boundary before deletion

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

* fix(pipelines): keep orphaned wm-fork-* workspaces ducklake-isolated

parent_workspace_id is ON DELETE SET NULL, so a fork can outlive its
parent with an empty ancestor chain while its cloned config still points
at the shared lake. Key the isolation gate on the wm-fork- prefix as well
as the chain (mirroring workspace_is_fork): orphaned forks get the write
redirect, registration and cleanup with zero ancestors (no defer), and
keep their 'fork' graph chips.

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

* fix(pipelines): attach orphaned wm-fork-* ancestors at their fork namespace

Chain position alone classified the last ancestor as a root, but an
orphaned wm-fork-* ancestor (its own parent deleted, SET NULL) ends the
chain the same way while its data lives in its fork namespace — its
descendants' defer views bound the dead root's lake instead. Key the
root-vs-fork decision on the wm-fork- prefix too, matching the
resolution gate.

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

* fix(pipelines): never inherit shared lake opt-out; durable cleanup ledger for failed fork deletions

- fork creation strips cloned fork_behavior stamps before applying the
  request's shared_ducklakes list: sharing is a per-creation choice, a
  fork of a shared fork defaults back to isolated
- fork_ducklake_namespace loses its ON DELETE CASCADE FK: rows are the
  durable cleanup ledger and outlive the workspace when physical cleanup
  fails post-commit; fork creation retries leftover rows for the reused
  id and refuses to create while a metadata schema still cannot be
  dropped (data-file leftovers alone are inert once the schema is gone
  and are swept by the next successful same-prefix cleanup)

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

* fix(pipelines): make orphaned-namespace cleanup retries independent of deleted fork resources

- ledger rows gain a schema_dropped phase flag: set when the schema drop
  succeeded but data cleanup failed, so later retries skip the schema
  phase and need no catalog credentials at all; registration resets it
  on re-attach (ON CONFLICT DO UPDATE) since attaching recreates the
  schema
- retry-path $res: resolution falls back to the workspace being forked
  (the deleted fork's resources were clones of a parent's); live paths
  (delete_workspace prepare, drop endpoint) pass no fallback

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

* fix(pipelines): fork tables from failed-after-commit runs stay fork-owned in defer and graph

A failed materialization must not disguise a physically existing fork
table as deferred: CREATE VIEW IF NOT EXISTS silently yields to the
table, so reads hit fork data while the graph claims parent defer.

- record_mat upsert preserves the last committed snapshot_id on failure
- defer discovery and graph chips treat fork rows with a committed
  snapshot as fork-owned even when status is failed
- inspect_fork_catalog also lists live fork tables (same round trip) and
  the defer list is filtered against them — covers rows recorded before
  this fix and tables created by raw SQL
- drop stale FK-cascade wording in the design doc and sidebar comment

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

* feat(pipelines): fork-mode ducklake settings — per-lake isolated/shared chips + banner, fork_behavior round-trip

The workspace-settings ducklake editor had no fork awareness: no
reminder of each lake's isolated/shared choice and no warning about
what edits mean in a fork. It also rebuilt each lake explicitly on
save, silently dropping fork_behavior — any settings save in a shared
fork flipped the lake back to isolated.

- fork detection mirrors the backend gate (parent link or wm-fork- prefix)
- info banner explaining isolated vs shared semantics in a fork
- per-lake chip (emerald 'isolated' / amber 'shared with parent') with
  tooltips, matching the pipeline graph chip colors
- fork_behavior added to DucklakeSettingsType and preserved through
  convertDucklakeSettingsToBackend

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 09:58:06 +02:00
Ruben Fiszel 33521505db feat(ducklake): scheduled lake maintenance (expiry, compaction, orphan cleanup) (#9916)
* feat(ducklake): scheduled lake maintenance (snapshot expiry, compaction, orphan cleanup)

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

* fix(ducklake): review fixes — starts_with not LIKE, CE license-lapse escape

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

* docs(ducklake): auth-contract docs + _unchecked rename per codex review

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

* refactor(ducklake): move maintenance payload construction into EE module

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

* fix(ducklake): fall through to script resolution for non-managed reserved-prefix schedules

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

* docs(ducklake): document accepted pre-existing-schedule limitation on the reserved prefix

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

* fix(ducklake): CE save-off clears the managed schedule row and queued occurrence

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

* chore: update ee-repo-ref to 2fab310d4f50ed7c34857d69c9b854f4491bf217

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

Previous ee-repo-ref: fff1fd830a36beba732486f05941ec243cf6b640

New ee-repo-ref: 2fab310d4f50ed7c34857d69c9b854f4491bf217

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-04 17:23:23 +02:00
Ruben Fiszel 42e11c6570 feat(pipelines): schema contracts — save-time consumer checks vs captured schemas (#9917)
* feat(pipelines): schema contracts — save-time consumer checks vs captured schemas

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

* refactor: move schemaContractContext above schemaCanEvolve doc comment

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

* fix: emit scd2/on_schema_change in CLI local graph, address review notes

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

* fix: gate editor _current ignore-suppression on scd2, matching backend

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 10:40:00 +02:00
Ruben Fiszel 5d7fb6deca feat(pipelines): asset freshness — fresh/stale badge (CE) + watchdog (EE) (#9909)
* feat(pipelines): passive asset freshness tracking on the graph

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

* chore(pipelines): drop dead freshness-enforcement stub, document query ordering

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

* feat(pipelines): freshness watchdog (EE) — auto re-run stale producers

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

* fix(pipelines): watchdog review fixes — archived workspaces, badge kind parity, scan index

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

* fix(pipelines): CI review — no singlestepflow in freshness, +N parity, completion-time fallback

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

* fix(pipelines): CI review — history completedAt, freshness/asset trigger UI metadata

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

* chore: update ee-repo-ref to 6f5fe0f7f56696fbef5a8349da38496c32e71666

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

Previous ee-repo-ref: 1f13380354bf591ae25a2c20d36917534bcc5459

New ee-repo-ref: 6f5fe0f7f56696fbef5a8349da38496c32e71666

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-04 06:23:44 +02:00
Ruben Fiszel df6e511763 chore(main): release 1.747.0 (#9901)
* chore(main): release 1.747.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-03 19:40:01 +02:00
hugocasa d600c7ecfe fix(ai): route Azure Foundry Claude models via Anthropic Messages API (#9908)
* fix(ai): route Azure Foundry Claude models via Anthropic Messages API

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

* fix(ai): keep explicit Azure OpenAI deployment base URLs intact

build_azure_openai_url only appends /openai/v1 for a bare resource root; any base with an explicit path (e.g. .../openai/deployments/<id>) is preserved. Adds a regression test and a unit test for usesAnthropicMessagesApi.

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

* feat(ai): enable Claude extended thinking on Azure Foundry

Route azure_foundry+Claude through the Anthropic reasoning branch (adaptive thinking + output_config.effort) instead of the gpt/o gate, and recognize claude-sonnet-5. Live-verified: sonnet-5 and opus-4-8 on Foundry accept the low/medium/high/xhigh/max ladder and render summarized thinking.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 18:26:12 +02:00
Ruben Fiszel 3ec1f164be fix: strip NUL characters from app values at save time (#9903)
App values are persisted to a json column, which permits the JSON NUL
escape (backslash-u-0000), but are later converted to jsonb (e.g. a
workspace fork clone_apps, search indexing), which rejects it with
"unsupported Unicode escape sequence" -- silently making the app
un-forkable. The usual source is a binary file such as .DS_Store
accidentally bundled into a raw app file map.

A real NUL is unstorable in jsonb either way, and frontend code that
needs the character writes it as the source escape (which JSON-encodes
to an escaped backslash + literal u0000 and is left untouched), so rather
than hard-failing the save we strip genuine NULs and warn.

Add strip_null_chars and apply it at both app_version insert sites
(create_app_internal and update_app_internal, covering the regular and
raw create/update routes). It removes a genuine NUL escape (odd run of
backslashes before u0000) while preserving an even run. Returns a
borrowed Cow (no allocation) when the value is already clean. Covered by
unit tests.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 15:58:56 +02:00
Ruben Fiszel fad5419b9d chore(main): release 1.746.0 (#9872)
* chore(main): release 1.746.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-03 01:03:44 +02:00
Ruben Fiszel 84141add1d feat(pipelines): workspace duckdb macro libraries (// macros / // use) (#9890)
* feat(pipelines): parse duckdb macro-library annotations (// macros, // use)

* feat(pipelines): duckdb macro registry tables + deploy-path validation and writes

* feat(pipelines): inject workspace duckdb macros into consumer jobs at run time

* feat(pipelines): surface macro libraries and lib-consumer edges in asset graph api

* feat(frontend): macro-library nodes, lib-consumer edges and scaffold in pipeline graph

* docs: mark dbt gap #7 (packages/macros) shipped via workspace macro libraries

* fix(pipelines): review fixes - char-safe parsing, local macros win, fork clone, trust-model docs

* feat(frontend): duckdb macro autocomplete + workspace macro explorer drawer

* fix(pipelines): address CI review - use-setup retention, splice past local defs, orphan filter, full consumer rescan, index-keyed strip

* fix(pipelines): inject provider library setup for implicitly-called macros too

* fix(pipelines): rls-gate macro listing + honor library-level // use transitively

* fix(pipelines): weave injected macros around local definitions by bind order

* fix(pipelines): injected library setup always runs before user blocks

* perf(pipelines): cache macro registry per workspace with notify-event invalidation

* perf(pipelines): disable macro registry cache on cloud
2026-07-03 00:59:30 +02:00
Ruben Fiszel af01e90b5c feat(s3): replace CE 50MB upload cap with 10GiB workspace storage quota (#9874)
* fix(s3_proxy): enforce CE 50MB upload cap on multipart uploads

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

* feat(s3): replace CE 50MB upload cap with 10GiB workspace storage quota

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

* fix(s3): gate CE quota OSS stubs to not(enterprise) to match callers

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

* fix(s3): delta-aware CE storage quota + guard usage-load retry loop

Account for the overwritten object's size in the quota check so valid
same-size overwrites near quota are not rejected (Codex review), and stop the
storage-usage $effect from re-firing on persistent API errors (Pi review).

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

* fix(s3): count chunked PUTs; revert overreaching volume quota copy

Volumes write to workspace storage via a separate worker-side path with its own
50MB-per-file cap that this PR does not change, so revert the drawer copy that
claimed they count toward the 10GiB quota (Codex review). Bump ee-repo-ref for
the chunked-PUT accounting fix.

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

* fix(s3): add SQLx cache for CE usage-bump/quota queries; exclude volumes

Regenerate the missing offline SQLx cache for the not(enterprise) bump and
remaining-quota queries so the private CE offline build compiles, and bump
ee-repo-ref for the volumes/-prefix exclusion from the counted quota (Codex
review).

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

* fix(s3): always HEAD for CE upload delta so overwrites don't inflate usage

Bump ee-repo-ref for the fast-path overwrite-accounting fix (Codex review).

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

* fix(s3): reserve volumes/ prefix on CE write surfaces to close quota bypass

Reject direct writes to the reserved volume prefix on the app-upload surface and
add the OSS stub; bump ee-repo-ref (Codex review).

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

* fix(s3): refuse new multipart work when CE workspace is at quota

Bump ee-repo-ref for the multipart-initiate/part quota gate (Codex review).

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

* feat(s3): reserve in-flight multipart bytes against CE storage quota

Add workspace_multipart_inflight table + grants, SQLx cache for the reservation
queries, and bump ee-repo-ref. Bounds abandoned multipart uploads that the
list-based recount can't see (Codex review).

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

* fix(s3): clear multipart reservation only after a successful complete

Add exclude-upload arg to the OSS quota stub/caller and the SQLx cache for the
updated remaining-quota query; bump ee-repo-ref (Codex review).

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

* fix(s3): per-part multipart reservation; commit only on part success

Per-part workspace_multipart_inflight schema (upload_id, part_id) so retries
replace rather than double-count; SQLx cache for the reworked queries; bump
ee-repo-ref (Codex review).

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

* perf(s3): HEAD the multipart overwrite target once per upload, not per part

SQLx cache for the stored-credit lookup; bump ee-repo-ref.

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

* chore: update ee-repo-ref to bea5a8b5120d6d69cab1ad4611ebe463559bd200

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

Previous ee-repo-ref: 6e6ff86f1939cf74736b7d435bf6851416437523

New ee-repo-ref: bea5a8b5120d6d69cab1ad4611ebe463559bd200

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-03 00:57:19 +02:00
hugocasa 7c7d7474cc feat: support workspace forks on cloud using parent workspace limits (#9864)
* feat: support workspace forks on cloud using parent workspace limits

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

* docs: clarify count_paid_seats approximates rather than mirrors billing seats

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

* fix: non-admin fork UI, attach cap, and fork-count for cloud forks

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

* fix: cloud fork billing cache on rename, usage display, attach cap edge

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

* feat: fork count in cloud quotas + fork billing points to parent

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

* fix: invalidate billing/fork caches on fork deletion for id reuse

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

* fix: gate fork usage remap on CLOUD_HOSTED, not just the cloud feature

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

* docs: note cloud feature vs CLOUD_HOSTED gating in backend guide

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

* fix: reserve fork-cap slots for an attach candidate's whole subtree

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

* fix: invalidate team-plan cache on delete, raise fork depth cap

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

* feat: cap fork nesting depth (MAX_FORK_DEPTH, default 5)

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

* fix: fork count/height robust to cycles and deleted intermediates

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

* fix(frontend): reset fork button loading state on creation error

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

* fix: invalidate billing cache for attached fork subtree; helper auth docs

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 23:59:02 +02:00
hugocasa d9b080f57f feat(ai): add Azure AI Foundry as a native AI provider (#9879)
* feat(ai): add Azure AI Foundry as a native AI provider

Adds `azure_foundry` as a new AIProvider variant wired through the AI
chat (copilot) and AI agent flow steps. Foundry's chat completions API
is OpenAI-compatible and uses Azure conventions (api-key header, Azure
URL building), so it reuses the existing OpenAI-compatible query builder
and proxy path via the shared `is_azure` helper (renamed from
`is_azure_openai`).

Backend (windmill-ai):
- New `AzureFoundry` enum variant (serde `azure_foundry`)
- `get_base_url` requires a resource base URL (like Azure OpenAI / Custom)
- `is_azure()` covers Azure OpenAI + Foundry (api-key auth, Azure URL)
- Added to OpenAI-compatible proxy support and HttpForward proxy mode
- New proxy URL unit test

Frontend (copilot):
- New provider entry, completion config, model-token handling, streamed
  usage tracking, and reasoning registry (all model-id-gated, so a no-op
  for Foundry's non-OpenAI catalog)
- Treated as a chat-completions provider, not the OpenAI Responses API

OpenAPI:
- `azure_foundry` added to AIProvider (openapi.yaml) and AIProviderKind
  (openflow.openapi.yaml); regenerated CLI guidance

Note: the `azure_foundry` resource type (base_url + optional api_key) is
hub-managed and must be published to the Windmill Hub separately.

Fixes WIN-2122

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

* fix(ai): add azure_foundry to copilot flow Zod provider enum

The tracked copilot flow schema (openFlowZod.gen.ts and its openFlow.json
source) still carried the old AIProvider enum, so validateFlowModules /
validateSpecialFlowModule rejected AI-generated flow edits that create or
update an aiagent module with provider kind "azure_foundry" before they
could be saved. Add the value to both (preserving the generated single-line
format) and a regression test over the flow-module validation path.

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

* feat(ai): lead provider list with OpenAI, Anthropic, Google AI

Reorder AI_PROVIDERS so the three primary direct providers come first. The
AIProviderPicker renders the first three entries as quick-access buttons, so
these become the defaults (previously OpenAI, Azure OpenAI, Azure Foundry);
Azure OpenAI / Azure Foundry stay adjacent right after. No logic depends on
provider order (only per-provider defaultModels[0] is read).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 23:42:44 +02:00
Ruben Fiszel 53bbb92953 feat(pipeline): backfill a range of partitions from the asset drawer (#9885)
* feat(pipeline): backfill a range of partitions from the asset drawer (ee)

* feat(pipeline): cancel in-flight backfill job and show cancelling state

* refactor(pipeline): move backfill range logic behind private feature

* fix(pipeline): close backfill cancel-launch race and record dispatch intent

* docs(openapi): producer_path also covers SDK write-edge producers

* chore: update ee-repo-ref to c3852ecb36bd0be1a74c63169e513888f3347850

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

Previous ee-repo-ref: 7c1450ef89fbc9e844a121b39cafe0d7235d704b

New ee-repo-ref: c3852ecb36bd0be1a74c63169e513888f3347850

Automated by sync-ee-ref workflow.

---------

Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-02 12:50:55 +02:00
Ruben Fiszel e77b7523a5 nit enterprise implies license feature 2026-07-02 06:01:22 +00:00
Ruben Fiszel 9a24cd2bef chore(main): release 1.745.0 (#9858)
* chore(main): release 1.745.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-02 00:15:58 +02:00
Ruben Fiszel 1a9debb689 fix(jobs): give flow dynselect a path and its worker tag, like scripts (#9867)
Fetching options for a `dynselect`/`dynmultiselect` input was inconsistent
between deployed scripts and deployed flows:

- scripts ran through `push_script_job_by_path_into_queue` — a `script` job
  with the script's path, tag, lock and codebase resolution;
- flows ran their schema dyn-select code as an anonymous `preview` with no
  path and no tag (always the language default), and reported access
  failures as a raw `SqlErr: no rows`.

Deployed scripts are left exactly as they were (that path already handles
tag/lock/codebase/on-behalf-of correctly). The flow branch now:
- carries the flow path on the preview job,
- reads the flow's `tag` under RLS and routes the job to it (falling back to
  the language default when unset), matching the script's worker group, and
- runs `check_tag_available_for_workspace` on that tag — the same gate a
  normal flow run and the script path apply — so a caller who can read the
  flow but is not allowed to use its (custom/scoped) worker tag is rejected
  consistently.

The flow's tag read runs on every request, so it also serves as the
per-request access check, replacing the raw error with a clean
`NotAuthorized` / `NotFound`. Entrypoint-name validation now covers all
branches (it is interpolated into the generated wrapper). Inline is
unchanged: a `preview` with no path on the language default, blocked for
operators.

Fixes WIN-2118

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 00:09:56 +02:00
Ruben Fiszel 20cd1a02d5 feat(forks): partial-visibility deploy + surface hidden items (#9868)
* fix(forks): let partial-visibility users deploy the visible subset

The fork Compare & Deploy page hid the deploy button entirely whenever the
comparison reported any item not visible to the user (all_ahead/all_behind
flags), telling them to hand the deploy to someone with full access. But the
non-visible items are already filtered out of the diff list, and the UI already
supports deploying an arbitrary subset via per-item selection — so blocking
everything was inconsistent and, for stale/phantom rows, blocked on items that
don't even exist.

Show the deploy footer regardless; the user acts on the visible/selected items
(the per-item disabled conditions are unchanged). The hidden-items notice is
kept but downgraded to a non-blocking, direction-scoped banner that explains the
excluded items instead of removing the action.

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

* feat(forks): surface hidden-item counts by kind + admin path list

WIP: expose items dropped by the visibility filter (hidden_ahead/hidden_behind
in the compare response): by-kind counts for everyone, kind+path only for admins.

* fix(forks): don't close the deployment request on a partial (hidden-items) deploy

Making the deploy button reachable in the partial-visibility case exposed a bug:
a clean merge-into-parent deploy unconditionally closed any open fork deployment
request as "merged" — marking its comments obsolete and notifying the requester
and assignees of a merge — even when hidden ahead changes were excluded from the
list and left undeployed. Only close the request as merged when the full ahead
set was visible (all_ahead_items_visible); otherwise leave it open (with a toast)
so someone with full access can finish it.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 23:52:34 +02:00
Ruben Fiszel 76a9523009 feat: use derived username instead of email for non-member superadmins (#9857)
* feat: use derived username instead of email for non-member superadmins

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

* fix: address review - drop redundant username cache, guard whoami membership by email

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

* refactor: use explicit non_member boolean instead of role string for superadmin banner

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

* fix: resolve email from password table for non-member superadmin permissioned_as

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

* fix: resolve non-member superadmin drafts via shared username->email resolver

Adds resolve_username_to_email (usr, then super_admin password fallback for both derived-username and email modes) and uses it in get_email_from_permissioned_as and the drafts get/list endpoints, so a non-member superadmin's drafts resolve and no email leaks into the drafts payload.

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

* test: superadmin-not-in-workspace schedule uses derived username as permissioned_as

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

* fix: resolve non-member superadmin identity in draft owner-circles, username_to_email, and home filter

Applies the password-fallback username resolution to the script/flow/app/draft owner-circle subqueries and the username_to_email endpoint (was an admins-workspace 'username == email' hack), and switches the home items-list user-folder filter to the non_member flag instead of the now-broken username-contains-@ heuristic.

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

* fix: backfill non-member superadmin favorites from email to derived username

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

* fix: propagate DB errors in username resolution instead of leaking email (CI review)

Addresses cubic-dev-ai P2: get_instance_username_or_fallback_to_email now returns Result and only falls back to the email for a genuine 'no derived username'; a query error propagates so callers fail closed rather than leaking the raw email as the acting username.

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

* fix: clarify non-member superadmin popover (username used + admin permissions)

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

* fix: keep username_to_email endpoint member-only to not disclose non-member superadmin email (CI review)

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

* fix: forbid disabling automate_username_creation once usernames assigned (CI review)

Makes the setting effectively one-way once instance-wide usernames exist, so the global-uniqueness invariant that keeps stored u/<username> identities (schedules/triggers/drafts/superadmin ownership) unambiguous can never be dropped back to workspace-local uniqueness. Re-saving false on an already-disabled instance stays a no-op.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:54:07 +00:00
Ruben Fiszel cfcc0b9453 chore(main): release 1.744.0 (#9839)
* chore(main): release 1.744.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-07-01 11:47:34 +02:00
Ruben Fiszel 74f579e6d9 feat(pipeline): local development for data pipelines (CLI --local + pipeline dev preview) (#9840)
* feat(pipeline): local development for data pipelines (CLI --local + pipeline dev preview)

Add the local edit→preview→run loop for data pipelines (folders of `// pipeline`
scripts), the analog of `wmill dev` / `wmill app dev`, usable from a code editor
or an agentic loop — without deploying.

No backend changes: full body inference comes from the same wasm the frontend
uses (windmill-parser-wasm-asset), which returns assets + pipeline annotations in
one call; local runs reuse runScriptPreview with _wmill_skip_asset_dispatch.

- localGraph.ts: wasm-backed working-tree → asset-graph builder (the enabler)
- pipeline show/run --local; new pipeline docs (PIPELINE.md/AGENTS.md) subcommand
- pipeline dev watcher + /pipeline_dev page (PipelineDevView) rendering the same
  PipelineGraphEditor from the pushed local graph, run via preview
- cascadeRun.ts: reusable run primitives extracted from the route page
- regenerated CLI agent docs

See docs/pipeline-local-dev.md for the full design, test steps, and handoff TODOs.
The live `pipeline dev` browser preview is implemented but not yet stack-verified.

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

* feat(pipeline): improve local dev preview (run, activity, responsive)

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

* feat(pipeline): dev-preview args, multi-root run, ws auto-reconnect

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

* fix(pipeline): connect managed-materialize producer in local dev graph

The CLI pinned windmill-parser-wasm-asset ^1.728.1, which predates managed-materialize support (added in 1.733.1); the frontend already pins 1.740.0. The CLI's wasm therefore never emitted `// materialize`, so the producer had no output edge and showed disconnected from its `// on` consumers. Bump the CLI to 1.740.0 (matching the frontend) and translate the parsed materialize target into the producer's write edge + materialize_target, mirroring frontend resolveGraph.ts.

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

* fix(pipeline): harden local-dev CLI (bare-.sql crash, defaultTs, docs clobber)

Review fixes, complementary to the dev-preview/materialize/multi-root work already
on the branch (none overlap those commits):

- localGraph: a bare `.sql` (no dialect) made inferContentTypeFromFilePath throw and
  abort the whole graph build — and wedge `pipeline dev` at startup. Skip the
  unclassifiable file instead. Also map `bunnative` → parse_assets_ts and add
  ruby/rlang/nu/powershell to the `#`-comment fallback.
- show/run/docs/dev: thread the resolved `wmill.yaml` defaultTs into the graph
  builder so `.ts` infers under the workspace's runtime (bun vs deno) instead of
  always bun — `opts.defaultTs` was always undefined (no such CLI flag).
- dev: wrap the startup graph build so a half-written file can't abort the watcher.
- docs: don't clobber a user-authored AGENTS.md/CLAUDE.md — only (over)write the
  pointer when absent or already a generated `@PIPELINE.md` pointer.

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

* fix(pipeline): bind dev WS to loopback + local-graph regression tests

- pipeline dev WS broadcast the folder's full script source (scripts[].content + temp_script_refs) unauthenticated on 0.0.0.0:3201 — bind 127.0.0.1 so it's not LAN-reachable (webview localhost + SSH/devbox port-forward still work).

- Add regression tests for the just-landed local-graph fixes: bare .sql is skipped (was a build/dev-startup crash), defaultTs threads into .ts runtime inference (bun vs deno), and #-comment languages (ruby) use the # annotation fallback.

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

* feat(pipeline): --frontend flag for pipeline dev page origin

wmill pipeline dev opens <remote>/pipeline_dev, but that route only exists in this build's frontend, so it 404s against a remote whose deployed frontend predates it. --frontend <origin> points the page at a locally-run frontend (REMOTE=<remote> npm run dev) while the API/token still target the remote — enabling the live preview against a real backend before the PR is deployed. No behavior change when omitted. Regenerated CLI agent docs.

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

* fix(pipeline): WS session token + details-pane live-reload refresh

Addresses CI review (Codex/Pi/Claude):

- dev WS: a browser tab could open ws://localhost:<port>/ws and receive the folder's full source (browsers don't enforce same-origin on WS, loopback bind alone doesn't help). Gate the upgrade on an unguessable per-session token carried in the dev-page URL (verifyClient → 401 without it). Verified: no-token/bad-token connections get 401 with no bundle.

- details pane: scriptRes keyed on [workspace, selection, draftScript] didn't re-run on a pipeline dev live-reload (same selection), so the open pane showed stale source. Thread a localScriptsVersion (the pushed bundle) into the key. Verified: editing a selected node's file updates the pane source without reselect.

- docs/pipeline-local-dev.md: refresh the stale 'not yet exercised' status + done TODOs.

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

* fix(pipeline): emit volume: annotation assets in local dev graph

Addresses CI review (Codex P1 / Pi P1): the wasm body parser doesn't surface `// volume: <name>` annotations — the frontend (infer.ts:parseVolumeAnnotations) and backend (asset_inference.rs) parse them separately and merge as rw volume assets. localGraph didn't, so a `# volume: cache` producer had no write edge and showed disconnected from its `// on volume://cache` consumer (and pipeline run --local wouldn't schedule downstream). Mirror the leading-comment-block scan (SQL excluded, matching both reference parsers) and merge into inferScriptAssets. Regression test added; verified producer -> volume://cache -> consumer connects.

Also (Codex P2): docs/pipeline-local-dev.md manual browser URL omitted the new ws_token param — without it the WS upgrade is rejected and the page sits disconnected. Doc now says to copy the URL the CLI prints (carries wm_token + ws_token) and recommends --frontend.

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

* fix(pipeline): runAll excludes event roots + review polish

Addresses CI review (Codex P1, Claude P2/P3):

- pipeline run runAll: derive the whole-pipeline selection from validStarts + descendants instead of all runnables, so an unqualified 'pipeline run <folder>' no longer fires event-trigger roots (kafka/mqtt/…) with empty args/side effects. Verified: a kafka root is excluded from the plan.

- cascadeRun.ts runBoundedCascade: use buildLineageDownstreamMap (read-aware) so a pure-reader runs after its producer, and return cyclic — parity with the route page's bounded run (the file is meant to be THE shared correct primitive).

- PipelineGraphEditor: storedRightPaneSize starts at 0 so the orientation-aware default (55% stacked / 40% side-by-side) actually applies on first open.

- localGraph fallbackParse (go/bash): scan only the leading comment header (no body-comment phantom triggers) and strip key=value options from the asset URI; regression test added.

- docs: reject '..' in the folder arg (it writes files under f/<folder>).

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

* fix(pipeline): route local previews to the // tag worker

Addresses CI review P1: the local graph/bundle dropped the parsed `// tag`, so a node annotated `// tag gpu` ran on the default worker in both `pipeline run --local` and `/pipeline_dev`, while the deployed pipeline routes it to that worker tag. Carry the tag through LocalScript / the pushed bundle / LocalScriptContent and pass it to runScriptPreview at all three launch sites. Verified: a duckdb node tagged `bash` produces a job tagged `bash`; regression test added.

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

* fix(pipeline): add asset partitions/schemas routes to OpenAPI, use generated client

The ducklake asset panels (PartitionStatusGrid, SchemaHistoryPanel) hit /assets/partitions and /assets/asset_schemas via raw fetch with cookie-only auth, because those backend routes were never added to openapi.yaml so the generated client had no methods for them. On /pipeline_dev (token-via-URL, no session cookie) the raw fetches 401'd. Add both GET routes + MaterializedPartition/AssetSchemaVersion schemas to openapi.yaml and call them through AssetService, which injects the bearer token, types, and cancellation automatically. Verified: Partitions + Schema tabs load in /pipeline_dev. (backfill stays a raw fetch — it's an EE-only route not in the OSS spec — with the token added inline.)

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

* chore(cli): regenerate bun.lock for windmill-parser-wasm-asset

package.json / package-lock.json carry windmill-parser-wasm-asset@1.740.0 but the tracked bun.lock (the CLI installs/builds/tests via bun) was stale, so fresh bun installs would resolve a different graph than the committed lock. Regenerated.

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

* fix(pipeline): show asset producer + its runs in the dev-preview panel

Selecting a ducklake/asset node in /pipeline_dev showed 'No producer for this asset' because selectionProducers wasn't passed (it's derived from the deployed graph on the route page, absent here). Compute it from the local graph's w/rw write-edges (incl. the // materialize target) and pass it through, mirroring the route page — so the panel shows the producing script and its (preview) runs, including data-test failures.

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

* fix(pipeline): carry annotation metadata onto local-graph runnables

The local graph emitted only path/usage_kind/in_pipeline/materialize_target per runnable, so /pipeline_dev and pipeline show --local weren't the same surface as the deployed graph for annotated scripts — missing the badges/lineage the shared canvas renders. Map the wasm-parsed partition_kind, freshness, tag, retry, data_tests, column_lineage, and materialize_strategy (derived append/merge/replace) onto each runnable, mirroring the deployed AssetGraphRunnableNode.

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

* fix(pipeline): exclude event handlers that are lineage descendants from runAll

The runAll guarantee ('never fires an event handler with empty args') only held for event ROOTS — validStarts excludes them, but runAll then unions in descendants(dag, start), so a kafka/mqtt/... handler that also reads an upstream pipeline asset (a lineage descendant of a valid start) still landed in the plan. Add eventTriggerScripts() and subtract it from the selection after the descendant union. +unit test.

Also: docs/pipeline-local-dev.md recipe used 'pipeline docs demo_pipeline' without --local (default queries the deployed graph → hits the empty hint); add --local.

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

* fix(pipeline): whole-pipeline run cuts at event handlers (drop their downstream too)

The prior runAll fix subtracted event handlers from the selection but left their downstream: for manual_root → asset_x → kafka_handler → asset_y → consumer, deleting only kafka_handler left consumer selected, and topoOrder then ran it as a root with missing/stale event-derived inputs. Replace the descendant-union+delete with reachableCutting(dag, validStarts, eventHandlers): traverse from valid starts but treat event handlers as cut points, so a node reachable ONLY through an event handler is dropped while one reachable via a non-event path stays. +unit test.

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

* fix(pipeline): recover // tag in the go/bash annotation fallback

The wasm path carries out.tag, but the go/bash fallback (and the wasm-error degradation path) only recovered pipeline + on, so a // tag gpu on a bash/go node — or a temporarily-unparseable ts/py/sql node — silently routed the local preview to the default worker while the deployed pipeline routes to the tag. Scan for // tag in fallbackParse too. +test.

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

* refactor(pipeline): extract shared assetProducers helper

The 'who writes this asset' write-edge derivation was copied verbatim in PipelineDevView and the pipeline route page — two copies that would drift. Extract assetProducers(graph, selection) into graphTraversal.ts and use it from both, keeping the dev view and route page in lockstep.

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

* fix(pipeline): only overwrite AGENTS.md/CLAUDE.md when it's the exact generated pointer

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

* fix(pipeline): wire local-dev runs into the selected-node runs pane

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

* fix(pipeline): exclude data_upload/webhook entrypoints from auto CLI runs

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

* feat(pipeline): --upload binds an object to a data_upload/webhook entry point

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

* feat(pipeline): add "Run + downstream" to the dev preview detail form

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

* fix(pipeline): cut non-autorun triggers on all run paths; multi-binding --upload

Address CI review: apply the data_upload/webhook/event barrier cut to the
single-root and bounded (--from/--to) paths, not just whole-pipeline; accumulate
repeatable --upload bindings per script (were overwritten); scope dev upload keys
by script+param to avoid basename clobbering; drop <script> from help text.

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

* fix(pipeline): reseed dev run form when a local edit changes the script's args

The read-only pane is keyed on script.path only, so in /pipeline_dev the selected
node re-resolves on every WS bundle without remounting; PipelineScriptView cloned
script.schema once, so adding/removing args left the run form on a stale schema
(could run with missing inputs). Extract PipelineRunForm (owns the SchemaForm
clone) and key it on the serialized schema: a real arg change reseeds the form,
an unchanged re-resolve keeps in-progress input.

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

* fix(pipeline): don't cut a scheduled/manual root that also has a non-autorun trigger

Address Codex P1: the barrier set subtracted only --upload-bound scripts, so a
script with both `// on schedule` and `// on data_upload` resolved as the start
yet was also a barrier — reachableCutting skipped it, giving an empty run plan.
Subtract all valid starts (schedule/manual roots + bound handlers) from barriers:
a legitimately-scheduled root runs on its schedule path even if it also carries a
caller-input trigger; pure input-only roots stay cut. Adds a regression test.

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

* fix(pipeline): deployed non-autorun enrichment, s3:// storage, --to cut accounting, tag regex

Address CI review (Codex P1/P1/P2, Pi P2):
- Deployed `pipeline run` recovers marker-only data_upload/webhook/email triggers
  from script bodies (like the `show` path) so input-only entrypoints are cut
  instead of auto-run empty on the deployed graph.
- `--upload s3://<storage>/<key>` keeps the named storage (authority) instead of
  folding it into the key, matching the S3Object round-trip convention.
- Bounded `--to` targets cut by a barrier are reported in droppedEnds (+warning),
  not reachableEnds.
- fallbackParse `// tag` matches a single token (\S+), rejecting multi-word prose.

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

* fix(pipeline): header-only deployed marker scan, fail-closed enrichment, default-storage s3 keys

Address CI review (Codex P2, cubic P1/P1/P2):
- Deployed marker recovery scans the LEADING comment header only (shared
  recoverHeaderMarkers helper, reused by the show enrichment too) so a body
  comment `// on data_upload` can't inject a phantom trigger and over-cut.
- Deployed run enrichment fails CLOSED: a script-body fetch error aborts the run
  instead of silently letting an input-only entrypoint run with empty args.
- Revert `--upload s3://` to default-storage whole-path keys (matching pipeline
  `s3://` asset-URI semantics); named-storage authority-splitting broke nested
  default keys like `s3://raw/2026/events.csv`.

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

* fix(pipeline): reject trailing content on fallback native markers; trim s3:/// key

Address CI review (Codex P2, cubic P3):
- fallbackParse now requires a native marker (`// on data_upload`) to stand alone;
  a line with trailing content (`// on data_upload f/foo`, `# on kafka topic`) is
  rejected, matching the canonical parser and keeping local/deployed parity.
- s3UriKey trims a leading slash so the canonical empty-authority default form
  `s3:///key` doesn't leak a leading slash into the object key.

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

* fix(pipeline): persist dev WS token per-port so reconnect survives a CLI restart

Address Codex P2: the /pipeline_dev auto-reconnect reuses the ws_token from the
page URL, but `pipeline dev` minted a fresh random token each start, so a restart
on the same port left the open page rejected by verifyClient forever. Persist the
token per-port under the user-private config dir (0600) and reuse it on restart,
so an already-open page reconnects — matching the reconnect behavior's intent.

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

* fix(pipeline): scope persisted dev WS token by workspace+folder+port

Address cubic P2: keying the persisted token by port alone let a stale browser
tab from a previous folder's session on the same port reconnect and receive a
different folder's source. Scope the token file by workspace+folder+port so a
same-session restart still reconnects, but a different folder on the same port
gets a distinct token that rejects stale cross-folder tabs.

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

* fix(pipeline): caller args can't override skip-dispatch guard; hash the dev token key

Address CI review (Codex P1, cubic P2):
- makeLaunch / CLI run build args with `_wmill_skip_asset_dispatch` LAST (and drop
  any caller-supplied copy) so a run-form/`--upload` arg can't re-enable backend
  asset dispatch while the client orchestrates the cascade (double-run / running
  deployed subscribers from a local preview). Adds a cascadeRun guard test.
- Dev WS token file key is a sha256 of NUL-delimited workspace+folder+port, so
  different folders (`a/b` vs `a_b`) can't collide onto the same token file.

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

* fix(pipeline): canonical s3://storage/key --upload parsing; scope dev token by remote+root

Address Codex P1/P1:
- Restore canonical S3Object URI parsing for `--upload` s3 sources, matching the
  frontend's `parseS3Object` (`s3://<storage>/<key>`, empty authority ⇒ default,
  `s3:///key`/`s3:///nested/key` for the default store). `s3://secondary/k.csv` →
  `{ s3: "k.csv", storage: "secondary" }` so a named-storage object is read from
  the right store. (This is the canonical convention; the default-storage nested
  key is served by the `s3:///` form.)
- Scope the persisted dev WS token by remote+workspace+root+folder+port (was
  workspace+folder+port), so two profiles on different remotes (or local checkouts)
  with the same workspace/folder/port don't share a token — a stale tab can't
  reconnect across a workspace/remote boundary.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:41:36 +02:00
hugocasa 1c46f899ca fix(mcp): stop double-escaping string query params in build_query_string (#9855)
MCP tool arguments were converted to URL query values via `value.to_string()`
+ `trim_matches('"')`. For string values containing JSON (e.g. the `args`/`result`
filters on job listing, `args` on schedule listing), `to_string()` JSON-encodes the
string and escapes inner quotes with backslashes; stripping the outer quotes leaves
`{\"k\":\"v\"}`, which the backend's `serde_json::from_str` then fails to parse,
falling back to `FALSE` and returning zero results.

Use `value.as_str()` to emit the raw string content for `Value::String`, falling
back to `value.to_string()` for non-string types (numbers, booleans). Adds
regression tests covering JSON-string, non-string, and plain-string params.

Fixes WIN-2114

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:42:42 +02:00
hugocasa b4b0c6a93e feat: add dev workspaces paired with a lockable prod workspace (#9793)
* feat: add dev workspaces paired with a lockable prod workspace

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

* fix: gate dev-workspace prod-lock on admin and prevent attach cycles

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

* feat: redirect locked-prod edits into the dev workspace

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

* fix: make dev-workspace settings tab available on CE (was EE-gated)

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

* feat: lock prod against forking too and funnel edits to the dev workspace

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

* feat: open dev item page on edit and tailor dev-workspace lock messages

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

* feat: prevent nested dev workspaces and hide dev option when one exists

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

* chore: drop the redundant already-has-dev hint on the fork form

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

* feat: badge dev workspaces and sort them ahead of forks in the tree/switcher

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

* feat: label dev workspaces as 'Dev workspace of X' instead of 'Fork of X'

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

* feat: label edit as 'Edit in <dev>', cover editor headers, auto-expand dev in tree

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

* feat: split prod lock into separate block-deploy and prevent-forking toggles

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

* feat: make resources/variables workspace-specific from compare page

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

* feat: steer AI-chat sessions to the dev workspace

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

* feat: refine session fork options and lock guidance for dev/prod

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

* fix: session picker reads prod's real rules, default to current ws

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

* feat: copy members into forks and clarify dev-workspace root labeling

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

* style: place the workspace id field under the fork name

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

* fix: address dev-workspace review findings and harden fork detection

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

* chore: regenerate sqlx offline cache

Restores entries dropped during the origin/main merge and adds the
dev-workspace queries (is_dev_workspace, ws_specific, has_parent).

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

* fix: address second-round dev-workspace review findings

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

* fix: address Pi and Codex review findings on dev-workspace endpoints

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

* fix: gate locked-dev git-branch fork on admin and validate ws_specific path

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

* fix: clear prod dev-lock when deleting an attached dev workspace

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

* refactor: consolidate dev-workspace migration and scope all-group join to attach

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

* fix: restore dev-workspace CHECK into consolidated migration and scope all-group join

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

* refactor: drop copy_members from the dev-workspace attach path

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

* fix: dev-workspace lifecycle/auth fixes from Codex review round

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

* feat: explicit create-in-other for workspace-specific items

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

* fix: make create-in-other strictly create-only (never overwrite target)

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

* fix: return 403 (not 401) for dev-workspace permission denials

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

* feat: allow attaching a same-family fork as a dev workspace

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

* style: emphasize the go-to-dev action in the no-direct-deploy alert

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

* fix: seed a resource's linked variables when creating it in the other workspace

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

* fix: judge workspace deploy/fork locks against the user's identity in that workspace

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

* style: clarify create-in help text in workspace-specific panel

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

* fix: admin-gate dev-workspace creation and harden lock/seed edges

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

* fix: preserve a staged fork's source on picker create-mode re-entry

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

* fix: clear dev flag on archive and check dev existence server-side

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

* fix: make create-in-other atomically create-only via direct create

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

* fix: create-only resource insert, ws-specific list scopes, archive lock guard

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

* fix: reserve the dev_workspace_lock protection-rule name from the public API

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

* docs: reattach create_protection_rule doc comment to its function

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

* refactor: make dev-archive pairing teardown atomic with the archive

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

* fix: follow deploy_to on root rename; show dev pairing to non-member prod admins

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

* fix: copy creator metadata on fork; invalidate fork routing cache on rename

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

* fix: accept g/ paths in set_ws_specific; gate copy_members to dev workspaces

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 17:01:07 +00:00
Ruben Fiszel a9ffdb996b chore(main): release 1.743.0 (#9837)
* chore(main): release 1.743.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-06-29 20:41:38 +00:00
Ruben Fiszel 96c0ff65bd chore(main): release 1.742.0 (#9830)
* chore(main): release 1.742.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-06-28 14:33:27 +02:00
Ruben Fiszel 75ba81b2d2 fix(audit): don't read pg_authid from an elevated context in S3 export migration (#9832)
* fix(audit): don't read pg_authid from an elevated context in S3 export migration

Migration 20260626132251 aborted instance startup on managed Postgres
(e.g. Cloud SQL) with "Modifying pg_authid or pg_auth_members is not
allowed in elevated context": the audit S3 export "oldest in-flight
xact_start" floor probe calls pg_has_role(...), which reads pg_authid,
and managed providers forbid that read from an elevated context. The
migration ran the probe inline in its UPDATE, so the whole migration —
and the instance boot — failed.

Extract the probe into a shared SQL function
audit_logs_s3_oldest_inflight_ts() that returns the oldest in-flight
xact_start (when cluster-wide stats are visible) or NULL otherwise. The
pg_has_role read is wrapped in a plpgsql BEGIN/EXCEPTION subtransaction,
so a pg_authid failure returns NULL (callers fall back to a conservative
7-day window / reject) instead of aborting. is_superuser (a GUC, no
catalog read) is checked first to short-circuit. The migration's trigger
and UPDATE, the OSS backfill try_start, and the EE exporter/startup
anchor (companion windmill-ee-private PR) all route through it.

Because 20260626132251 already shipped, it is added to the
potentially_stale list in windmill-api/src/db.rs: on startup the stale
_sqlx_migrations row (checksum mismatch) is deleted and the fixed,
idempotent migration re-applies, so already-migrated instances upgrade
without a checksum-mismatch boot failure.

Fixes WIN-2108

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

* chore: update ee-repo-ref to 95352c13c4c82247d8cfd80936f9203aeb079802

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

Previous ee-repo-ref: 136f49a52af922868acac33abf8198913a9e835c

New ee-repo-ref: 95352c13c4c82247d8cfd80936f9203aeb079802

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-06-28 14:21:38 +02:00
Ruben Fiszel 9172a0945b chore(main): release 1.741.0 (#9804)
* chore(main): release 1.741.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-06-26 21:43:02 +02:00
Ruben Fiszel 577ceeee86 perf(audit): re-anchor S3 audit export on enable + opt-in backfill (#9818)
* [ee] perf(audit): re-anchor S3 audit export on enable + opt-in backfill

The S3/GCS audit-log export's steady-state query filters by `age(xmin)`
(unindexable), so the only scan bound is the timestamp floor. On a fresh
enable the floor was epoch, and on a re-enable the cursor resumed from its
pre-disable position — either way the first run scanned the whole
`audit_partitioned` table. Under a `statement_timeout` (e.g. Aiven) that scan
never completes: the cursor never advances, nothing is exported, and the
repeated full scans saturate the database.

Re-anchor on enable (EE companion, windmill-ee-private#634):
- New trigger migration records a recent timestamp floor instead of the epoch
  sentinel and `DO UPDATE`s the cursor to the current snapshot xmin on
  re-enable, so the export always resumes from ~now and never rescans history.
  Includes a one-time fixup for legacy epoch-sentinel checkpoints on upgrade.

Opt-in historical backfill (new `audit_logs_s3_backfill` module + endpoints):
- Exports a chosen `[from, to)` window on demand, scanning strictly by
  `timestamp` (the partition key) in bounded keyset pages — each query is an
  index scan capped at one page (verified via EXPLAIN: later partitions
  `never executed`, ~11ms/page), so it stays well under any statement timeout
  regardless of window size. Writes alongside the steady-state objects under
  logs/audit/, without touching the xmin cursor.
- POST /settings/audit_logs_s3_backfill {from,to} (super-admin + Enterprise),
  GET /settings/audit_logs_s3_backfill_status.

Also repurposes the status endpoint's `bootstrapping` flag to mean "draining a
backlog" (the cursor is capped and catching up), and updates the setting
description to point operators at the backfill.

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

* fix(audit): heartbeat backfill lease per object; bump EE ref

Address review (cubic): persist progress (refreshing the lease heartbeat) after
every object PUT in the backfill page loop, not only once per page, so the gap
between heartbeats stays well under STALE_HEARTBEAT_SECS even on slow uploads
and another replica can't re-claim mid-page and run a concurrent backfill.

Bumps ee-repo-ref.txt to pull in the EE test-race fix (folding the backlog-drain
regression into the single audit e2e test).

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

* fix(audit): reject unstable backfill windows; bump EE ref

Address review (P1): the backfill keyset-pages over rows visible at scan time
and declares completion when the scan runs dry, but a row's `timestamp` is its
inserting transaction's `xact_start`. A window whose upper bound is recent or in
the future could silently omit a transaction that started inside `[from, to)`
but commits after the scan passed that timestamp. `try_start` now rejects any
`to` newer than the oldest in-flight `xact_start` (everything strictly older
than the oldest running transaction is committed and stable), using the same
trustworthy stats gating as the exporter's floor (restricted role / 2PC → a
7-day-old cutoff).

Bumps ee-repo-ref.txt for the EE monotonic-checkpoint fix.

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

* fix(audit): re-anchor legacy epoch checkpoints instead of synthetic floor

Address review (P1): the legacy-checkpoint fixup stamped last_oldest_inflight_ts
to now()-7d while leaving the old last_xmin in place. On an instance that
enabled export on the old code >7 days ago and got stuck before the first
successful batch, the next run would filter post-enable rows older than 7 days
out via `timestamp >= ts_floor` while still advancing last_xmin over the
interval — silently dropping them (the same floor-vs-cursor loss class fixed
elsewhere in this PR), and contradicting the "nothing committed after enabling
is skipped" guarantee.

A stuck epoch-sentinel checkpoint cannot be safely resumed (its backlog can be
arbitrarily old, so any recent floor prunes rows the cursor then skips, and an
epoch floor reintroduces the full scan). Re-anchor it to the migration's current
snapshot xmin instead — exactly like a fresh enable — so the export resumes
cleanly from ~now and the never-exported pre-upgrade window is recovered via the
opt-in backfill rather than silently dropped. Reword the setting description so
it no longer implies the disabled/legacy window is covered by the cursor.

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

* test(audit): end-to-end integration tests for the object-store backfill

The backfill previously had only SQL-level/EXPLAIN validation. Add real
integration tests (in-memory object store, sqlx::test) exercising the public
path:

- backfill_exports_window_in_pages: with the page size forced to 2 rows, a
  settled 3-day window is exported across multiple keyset pages; asserts every
  in-window row lands exactly once, rows outside [from,to) are excluded, a day
  that straddles a page boundary yields more than one object, progress counts
  match, and a re-run is idempotent (deterministic keys overwritten, no dupes).
- backfill_rejects_unstable_window: a future/live `to` is rejected as unstable,
  a window safely in the past is accepted.

Adds a test-only PAGE_ROWS override so multi-page behaviour is exercised with a
handful of rows.

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

* docs(audit): note backfill scope is audit_partitioned only

Make explicit that, like the steady-state export, the backfill reads only
audit_partitioned; the pre-partitioning `audit` table is intentionally out of
scope (not a missed case).

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

* fix(audit): reject backfill windows before the partitioned boundary

Address review (Codex P1): the backfill reads only audit_partitioned, but
pre-partitioning history lives in the legacy `audit` table (still read by audit
list/get via UNION ALL, and retained for the configured period — 365 days by
default on EE). Since the setting text points operators at this API for
"pre-existing history", a window overlapping legacy rows would report completion
while silently omitting them.

Per the decision to not export the legacy table, reject instead of silently
omit: try_start now rejects a `from` earlier than the oldest audit_partitioned
timestamp (every legacy row predates the partition cutover, so a `from` at/after
that boundary can never overlap them). Reworded the setting text to scope the
backfill to the partitioned era. Added a regression test, plus an RAII guard
(cubic P2) so the test-only globals are restored even if an assertion panics.

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

* fix(audit): backfill object keys per-window; require trustworthy settled cutoff

Address review (two P1s):

- Object-key overwrite loss: keys were `dt=<day>/audit_backfill_<min_id>.ndjson`.
  A narrower, overlapping backfill can start a day's page at the same first row
  (same min_id) but hold fewer rows, and `put` would overwrite a broader run's
  object — silently dropping the rows only that object held. Include the
  requested window in the key so different ranges write disjoint objects (same
  window re-runs stay idempotent; consumers dedupe overlapping rows by id). New
  regression test (verified red→green).

- Untrustworthy settled cutoff: when min(xact_start) isn't trustworthy (role
  lacks pg_read_all_stats/superuser, or a prepared 2PC txn exists), the old
  now()-7d fallback could still let an old transaction commit rows inside an
  accepted window after the scan, so a "complete" backfill silently missed them.
  Since a backfill asserts completeness, reject in those cases instead of
  falling back. (The continuous exporter keeps its 7-day fallback — it only
  claims bounded lag.)

Also makes the tests robust under the parallel runner: run_backfill takes the
store as a param, so tests pass a local in-memory store (no global
OBJECT_STORE_SETTINGS race) and serialize on the PAGE_ROWS override.

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

* fix(audit): reject backfill overlapping legacy table; regen deref openapi; trim migration comment

Address review (1 P1 + 2 P2):

- Empty-partition backfill (P1): the min(audit_partitioned) guard no-ops when
  audit_partitioned is empty, so an upgraded instance with legacy `audit` rows
  but no partitioned rows yet would accept a window and complete with zero rows,
  silently omitting the legacy rows. Check the legacy `audit` table directly:
  reject any window that overlaps a legacy row (subsumes the boundary check and
  covers the empty-partitioned case). Test updated accordingly.

- openapi-deref (P2): regenerate openapi-deref.yaml/json (served via include_str!)
  so /openapi.{yaml,json} expose the new backfill endpoints.

- Migration comment (P2): trim the PR-history narration to the durable
  constraints (why a recent floor and a monotonic cursor are required), per
  AGENTS.md.

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

* chore: update ee-repo-ref to b821fecccbcba2efed544890576bf2b84321d70d

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

Previous ee-repo-ref: 6b191b77aabcf77658ad4f9031576e0d7b66bf89

New ee-repo-ref: b821fecccbcba2efed544890576bf2b84321d70d

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-06-26 21:37:33 +02:00