Commit Graph

13922 Commits

Author SHA1 Message Date
Ruben Fiszel 77222a24ec fix: relabel materialization timeout and document per-phase budget 2026-07-16 12:02:56 +00:00
Ruben Fiszel c9d6c59ed8 fix: keep job ping alive while materializing (s3object) SQL args 2026-07-16 11:55:20 +00:00
Ruben Fiszel a935d06c8e chore(main): release 1.760.1 (#10142)
* chore(main): release 1.760.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.760.1
2026-07-15 21:29:39 +02:00
Ruben Fiszel 8c725d9e44 fix(apps): honor presigned S3 signature on app display/preview routes (#10141)
The app provenance gate short-circuits on a valid presigned signature, but only the raw download_s3_file route parsed it. The parquet/csv/table-count/file-preview/metadata routes discarded sig/exp and always fell through to the provenance gate, so a presigned S3 object rendered as a table showed "File restricted" for any viewer who did not produce it. Thread sig/exp through every apps_u S3 display route and forward the presigned bearer from ParqetCsvTableRenderer/DisplayResult.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:18:15 +02:00
Ruben Fiszel 2092155191 chore(main): release 1.760.0 (#10128)
* chore(main): release 1.760.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.760.0
2026-07-15 18:00:53 +02:00
Guilhem 9705d60284 fix(frontend): keep session-exit URL clean by syncing new_draft strip with the router (#10101)
* fix(frontend): keep session-exit URL clean by syncing new_draft strip with the router

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

* chore(frontend): correct replaceState comment and test-mock wording per review

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

* docs(frontend): correct replaceState comment and drop drafting-history phrasing

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:56:19 +02:00
Guilhem af177cefe0 fix(frontend): graceful small-screen timeframe picker on the runs page (#10073)
* fix(frontend): prevent runs timeframe calendar popover overflow on small screens

The Runs page timeframe picker rendered its popover as a wide 3-column row
(preset list + two side-by-side calendars). With the right-aligned trigger and
a center-anchored `bottom` placement, the popup ran off the right edge on
narrow viewports.

Anchor the popover to the right edge (`placement="bottom-end"`) and make its
content reflow to a vertical stack below the `sm` breakpoint, capped at
`max-w-[calc(100vw-2rem)] max-h-[80vh] overflow-auto` so it can never exceed the
viewport. The desktop side-by-side layout is unchanged.

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

* feat(frontend): compact runs timeframe picker with a Start/End toggle on small screens

The two-calendar desktop popover needs ~780px (two min-w-9 grids + presets +
popover padding); below that it overflows. Under 800px, show a single calendar
with a Start/End toggle picking which bound it edits, using set-start/set-end so
each bound keeps its date and HH:MM time inputs — the same precision the desktop
start/end pair offers.

On short/landscape viewports the compact panel is scroll-contained within the
popover's fitViewport height (contentClasses overflow-y-auto, scoped to the
small layout) so its lower controls stay reachable. The desktop two-calendar
layout is unchanged.

Presets are shared between both layouts via a snippet, and the active range is
preserved across the breakpoint since both branches drive the same value.

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

* feat(frontend): let InlineCalendarInput month/year selects portal, use in compact timeframe picker

Add an opt-in `portalSelects` prop to InlineCalendarInput that portals the
month/year dropdowns to the body (default keeps them in-flow, so existing
consumers are unchanged). The compact runs timeframe picker enables it so the
dropdowns escape its scroll-contained (overflow-y-auto) popover instead of
being clipped.

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-15 17:55:21 +02:00
Diego Imbert 2fe999f66c fix(frontend): treat a displaced draft save as superseded, not failed (#10094) 2026-07-15 17:55:01 +02:00
hugocasa 8bfe5c9340 fix(ai): stop sending the AI agent system prompt twice for OpenAI (#10126)
* fix(ai): stop sending the AI agent system prompt twice for OpenAI

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

* fix(ai): document collect_system_prompt precedence and trim duplicate comments

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

* fix(ai): hoist only the leading system prompt for OpenAI

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-15 17:53:09 +02:00
Ruben Fiszel 17872018cc feat(nsjail): make python/ansible rlimit_as configurable per worker (GIT-921) (#10138)
nsjail caps a jailed job's virtual address space at rlimit_as (4096 MiB for
python3 and ansible). JIT runtimes (Bun/JavaScriptCore, the JVM) reserve large
virtual ranges up front, so a subprocess spawned from a jailed Python/Ansible
job can crash against this cap even when its physical memory use is modest
(e.g. the Bun-compiled claude CLI hitting JSC/pthread allocation failures).

Most other language protos already run with disable_rl: true (unlimited);
python3 and ansible are the outliers with an explicit rlimit_as. This exposes
that cap via a per-language env var (NSJAIL_PY_RLIMIT_AS_MB,
NSJAIL_ANSIBLE_RLIMIT_AS_MB) so operators can raise or lift it on a dedicated
worker pool without a source patch/rebuild and without weakening the
mount/PID/user-namespace isolation that provides the real security boundary.
Only the address-space limit changes; cpu/fsize/nofile rlimits are untouched.

Value is in MiB, or unlimited/none/inf/0 to uncap (rlimit_as_type: INF). Unset
keeps the historical 4096 default.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:52:39 +02:00
Ruben Fiszel f7eb5c460d fix(apps): invalidate cached app policy on change or deletion (GHSA-r5v4-cxh9-7qhq) (#10121)
* fix(apps): invalidate cached app policy on change or deletion (GHSA-r5v4-cxh9-7qhq)

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

* docs(agents): keep PR tests and comments minimal and non-ephemeral

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-15 17:41:54 +02:00
hugocasa bd3adc9781 fix(frontend): only carry custom-tag overrides on 'Run again' (#10137)
* fix(frontend): only carry custom-tag overrides on 'Run again'

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

* fix(frontend): keep literal overrides on dynamic-tag reruns

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:33:42 +02:00
Ruben Fiszel 5626768471 fix(cli-image): patch fixable CRITICAL CVEs in windmill-cli image (GIT-922) (#10135)
The published ghcr.io/windmill-labs/windmill-cli image shipped two fixable
CRITICAL findings:

- openssl (libssl3t64, openssl-provider-legacy): stale in the oven/bun:slim
  base image (CVE-2026-34182). Fixed by running apt-get upgrade so the image
  picks up the patched Debian packages.
- vitest 2.1.9 (CVE-2026-47429 / GHSA-5xrq-8626-4rwp): a dev-only
  devDependency reference in esrap's cached package.json living in bun's
  package download cache. The cache is unused at runtime, so it is removed
  after install.

Validated by building the image and scanning with Trivy: openssl now reports
3.5.6-1~deb13u2 (fixed) and vitest is entirely absent. wmill still runs. The
only remaining CRITICALs are perl-base CVEs with no upstream fix available.

Fixes GIT-922

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 15:21:16 +02:00
Ruben Fiszel 188647a942 fix(security): enforce variables:write scope on resource-delete var cascade (GHSA-xmr2-98m6-cjf7) (#10123)
A token scoped only to resources:write:<path> could delete linked secret
variables it had no variables:write scope for, by embedding $var:<victim>
in an attacker-controlled resource value and triggering the resource-delete
cascade. #9712 re-enforced scoped-token boundaries broadly but missed this path.

Add check_linked_var_delete_scopes, called before the cascade in both
delete_resource and delete_resources_bulk: require variables:write for every
linked variable, failing (and rolling back) the delete otherwise. No-op for
unscoped tokens, so full-token cascade cleanup is unchanged.

No co-located-path exemption: a resource and a variable may share a path, and a
resource-write token can create a resource over an existing standalone variable
and self-reference it, so "same path as the deleted resource" is attacker-
forgeable and cannot stand in for variable scope.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:19:17 +02:00
Ruben Fiszel 6407d9ff5c fix(bash): normalize CRLF line endings before running scripts (#10131)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:46:26 +02:00
Ruben Fiszel 27ead8d084 fix(ai): disable redirects on worker AI provider client (GHSA-5q4v) (#10122)
* fix(ai): disable redirects on worker AI provider client (GHSA-5q4v)

The worker AI request path issued provider requests with the shared
HTTP_CLIENT, which follows up to 10 redirects without revalidating each
hop. SSRF validation on the provider base_url is single-shot, so a public
base_url could 3xx the worker into a private/internal host (e.g. cloud
metadata), bypassing the private-endpoint protection. The API proxy was
already hardened in #9370; the worker path was missed.

Add a dedicated AI_HTTP_CLIENT with redirects disabled and use it for the
user-controlled provider endpoint, mirroring the API proxy client.

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

* fix(ai): add ALLOW_AI_BASE_URL_REDIRECTS debug escape hatch

Off by default (redirects stay disabled). When set, restores redirect
following on the AI HTTP client for debugging non-standard/self-hosted
gateways, with a startup warning that it weakens SSRF protection.

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

* docs(ai): correct redirect comment for the escape hatch override

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

* docs(ai): condense redirect invariant comments per review

Anchor the SSRF rationale to ALLOW_AI_BASE_URL_REDIRECTS (the knob that
would break it) and shorten the AI_HTTP_CLIENT and call-site comments to
avoid restating it at multiple sites (AGENTS.md).

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-15 11:24:26 +02:00
Ruben Fiszel 73c8d7f08a fix: reject git URL fragment/query SSRF bypass (GHSA-p5cj-8cfh-mjv6) (#10120)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 11:07:04 +02:00
Ruben Fiszel 360e783b1d chore(main): release 1.759.0 (#10108)
* chore(main): release 1.759.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.759.0
2026-07-15 10:02:01 +02:00
hugocasa ebe31aeeac feat(dev-workspace): reflect existing protection rules in lock toggles (#10093)
* feat(dev-workspace): reflect existing protection rules in lock toggles

When creating or attaching a dev workspace, the "block direct edits" and
"prevent forking" toggles now check the root workspace's current protection
rules. If a restriction is already enforced by an existing rule, its toggle is
shown on but locked, with a note, instead of offering a fresh default that could
misrepresent the effect. The value sent to the backend is derived so it stays
consistent with what the locked toggle shows.

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

* docs: clarify fail-open comment on dev-workspace lock toggles

Reword the protection-rule fetch comment so the fallback path isn't misread as
dropping protection: a failed fetch falls back to the editable default-on
toggle, and any real rule still enforces server-side.

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

* fix(dev-workspace): lock protection toggles until rules load

The lock toggles derived alreadyBlocks* from an async fetch, so during the load
window (and the first frame before loading flips) they were editable and the
effective value could be false. A user could turn a lock off and submit before
an existing rule was detected, omitting the reserved rule and silently leaving
prod unprotected once that existing rule was later removed.

Treat "rules not yet known" (loading || current === undefined) the same as
"already enforced": lock the toggle on and keep the effective value true during
that window, so the request can never submit false before the fetch resolves.
Submission stays available (a hung fetch degrades to over-protection, not a
blocked form). Also fixes the stale-value flash when switching base workspace.

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

* fix(dev-workspace): honor rule bypasses and guard stale protection fetches

Two issues in the protection-rule awareness for the dev-workspace lock toggles:

- Bypassable rules became unconditional locks. alreadyBlocks* used
  isRuleActiveInRulesets, which ignores bypass_users/bypass_groups, and forced
  the request flag to true. The reserved dev_workspace_lock rule is created with
  empty bypass lists, so layering it over an existing rule that let specific
  users through revoked their deploy/forking access. Switch to
  isRuleUnconditionallyActiveInRulesets so a toggle is only shown as already
  enforced (locked) when an existing rule has no bypasses; a bypassable rule
  stays editable, making the lock the user's explicit choice.

- A stale protection fetch could apply another base's rules. The generated
  client can't take an abort signal, so a delayed response for a previous base
  could overwrite the newly selected one. Tag each result with its workspace and
  only trust a result matching the current base; also throw AbortError from a
  superseded fetch so it can't overwrite current.

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

* docs: condense protection helper comment to four lines

Trim the isRuleUnconditionallyActiveInRulesets doc comment to satisfy the
AGENTS.md ≤4-line comment rule.

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

* fix(dev-workspace): align already-enforced note under the toggle label

The note used ml-8, landing under the toggle switch rather than aligned with
the switch edge or the label, so it read as floating. Bump to ml-11 so it lines
up under the label as helper text for that toggle.

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-15 10:01:35 +02:00
Ruben Fiszel cab3430e64 ci: link backend integration tests with mold to fix OOM (exit 143) (#10103)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:00:50 +02:00
Kobi Hikri 91b5a10504 ci: pin cpina/github-action-push-to-another-repository to a full commit SHA (#10119)
go_on_release.yml referenced this third-party action by the mutable @devel
branch in the step that holds secrets.DENO_PAT (a write-scoped PAT used to push
the generated go-client to another repo). Pinning to a full commit SHA (v1.7.3)
removes the mutable-ref supply-chain exposure, consistent with the other
SHA-pinned actions in the repo.
2026-07-15 09:52:50 +02:00
Ruben Fiszel eb7a2e048b ci: cap build jobs and disable incremental in backend tests to prevent OOM (#10118) 2026-07-15 09:50:16 +02:00
Ruben Fiszel 6d1e12d5e9 feat(nativets): expose the standard web-platform globals deno_web provides (#10112)
* feat(nativets): expose standard web-platform globals for bun parity

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

* feat(nativets): wire bun-present Event subclasses and add construction smoke test

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

* fix(nativets): seed performance.timeOrigin per isolate, drop broken reportError

Addresses CI Codex review on #10112:
- performance.timeOrigin was undefined (setTimeOrigin never called); seed it
  per isolate via __wmInitPerIsolate executed from create_nativets_runtime.
- reportError needs a global EventTarget this runtime never installs; drop it.
- reword the namespace-import comment to not describe drafting history.

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

* feat(nativets): wire DOMException global + broad edge-case smoke sweep

DOMException is present in bun and, more importantly, deno_web references it as
a global: AbortController.abort() with no reason constructs a
DOMException("...", "AbortError"), so the already-wired AbortController/
AbortSignal threw "DOMException is not defined" on abort. Surfaced by a new
functional edge-case sweep (smoke_web_globals_edge_cases) that exercises every
wired global for real (not just presence) — DOMException/abort, AbortSignal.timeout,
EventTarget dispatch, stream tee/reader/writer, all 3 compression formats,
structuredClone Map/Set/Date/circular/reject-function, performance mark/measure,
MessagePort delivery — plus a check that the merged Web Crypto globals still work.

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

* test(nativets): restore arg-default smoke tests dropped in merge, drop history comments

Addresses CI Codex/Pi review on the merge commit:
- Merge conflict resolution (checkout --ours) dropped smoke_missing_optional_arg_uses_default
  and smoke_explicit_null_arg_is_preserved (added on main by #10111); restore them.
- Reword edge-case-sweep comments to state the constraint, not how the gaps were found.

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

* fix(nativets): give reportException a global dispatch target; wire stream reader/controller globals

Addresses CI Codex review on #10112:
- P1: a throwing EventTarget listener (and reportError) is routed through
  deno_web's reportException, which dispatches on a saved global reference.
  With none set, dispatchEvent threw a masking error that hid the original.
  Wire a dedicated EventTarget as that target so the ORIGINAL error is reported
  (async unhandled, matching bun). Does NOT make globalThis an EventTarget (bun's
  isn't either). Re-adds reportError, now functional. Regression test asserts the
  original error is surfaced, not a masking one.
- P2: wire the stream reader/controller globals bun also exposes
  (ReadableStreamDefaultReader/BYOBReader, ReadableStreamDefault/ByteStreamController,
  ReadableStreamBYOBRequest, WritableStreamDefaultWriter/Controller,
  TransformStreamDefaultController) for instanceof parity; sweep verifies via real
  reader/writer/controller instances.

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

* fix(nativets): make globalThis an EventTarget so globalThis.reportError() works

Addresses follow-up CI Codex review on #10112:
- P1: the prior fix saved a *separate* EventTarget as the global reference, so
  globalThis.reportError() still failed its receiver check (this === globalThis_)
  with 'Illegal invocation'. Make globalThis itself the saved reference by turning
  it into a functional EventTarget (setPrototypeOf to DedicatedWorkerGlobalScope +
  setEventTargetData + webidl brand + saveGlobalThisReference), per isolate in
  __wmInitPerIsolate. Both reportError(e) and globalThis.reportError(e) now surface
  the original error (async, matching bun) instead of throwing. New test
  smoke_report_error_both_call_forms covers both call forms.
- P2: reword the regression-test comment to state the invariant, not the patch history.

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

* feat(nativets): wire performance constructor globals for bun parity

Addresses the P2 nit in the CI Codex review: bun exposes Performance,
PerformanceEntry, PerformanceMark, and PerformanceMeasure as globals (deno_web
exports all four), so wire them alongside the performance singleton. The
edge-case sweep verifies instanceof against real mark/measure entries.

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

* docs(nativets): state global-wiring comment as a constraint, not patch history

Addresses the P2 in the CI Codex review: reword the block comment to describe
the current bun-parity constraint and the deliberate EventSource/ImageData
exclusions, without narrating what was or wasn't wired before (per AGENTS.md).

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-15 09:47:54 +02:00
Ruben Fiszel 95d9ff02ee fix(jseval): raise QuickJS eval memory cap to 128MB with clear OOM error (#10116)
* fix(jseval): raise QuickJS eval memory cap to 128MB with clear OOM error

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

* docs(jseval): note bare null/undefined throws are absorbed into OOM bucket

Addresses CI review P2 nit on map_quickjs_error.

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

* fix(jseval): keep batch-rerun eval on a conservative 32MB cap; tighten OOM match

Addresses CI review: eval_simple_js runs in the API process with unbounded request concurrency, so it must not inherit the raised flow-transform cap. Tighten the Exception OOM match to exact string. Reword drafting-history comments per AGENTS.md.

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

* fix(jseval): gate OOM on InternalError kind; path-specific remediation hint

Require the OOM InternalError name (not just the message) so a user throw new Error('out of memory') is not misclassified, and only suggest QUICKJS_MEMORY_LIMIT_MB on the env-tunable flow path (not the fixed-cap eval_simple_js path).

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-15 09:35:55 +02:00
Ruben Fiszel a6191e2a85 fix(mcp): advertise flow input variables in MCP tools (#10117)
Flow input schemas omit `required` (they carry an `order` key instead),
which made `serde_json::from_str::<SchemaType>` fail in
`convert_schema_to_schema_type`. The error was swallowed and callers fell
back to an empty `SchemaType::default()`, so MCP flow tools advertised no
inputs. Add `#[serde(default)]` to `type`, `properties`, and `required` on
`SchemaType` so these schemas deserialize correctly. Scripts always include
`required` and were unaffected.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:04:50 +02:00
Ruben Fiszel e9fd4e7554 perf(rls): wrap session GUC reads in RLS policies for per-statement InitPlan (GIT-919) (#10110)
* perf(rls): wrap session GUC reads in RLS policies for per-statement InitPlan

RLS policies read current_setting('session.user' / 'session.groups' /
'session.pgroups' / 'session.folders_read' / 'session.folders_write')
directly inside their USING / WITH CHECK predicates. Postgres treats those
unwrapped calls as potentially row-varying and re-evaluates them once per
scanned row, on the read path of every workspace-scoped table.

The GUCs are set with SET LOCAL (set_config(..., true)) in
set_session_context(), so they are constant for the duration of a statement.
Wrapping each session-derived subexpression in a scalar sub-select lets the
planner hoist it to a one-time InitPlan (evaluated once per statement, reused
for every row) — same rows in, same rows out, N per-row GUC lookups collapse
to 1. Array-producing subexpressions keep an explicit ::text[] cast on the
sub-select so `= ANY (...)` / `?|` stay in their array-operand form rather
than being reparsed as a row-returning subquery.

The consolidating migration recreates every existing policy (across ~30 prior
migrations) whose predicate reads a session GUC, by deparsing the current
predicate and substituting the wrapped forms; the down migration is the exact
inverse (byte-identical round-trip). The adding-a-trigger skill documents the
wrapped form so new trigger tables inherit it.

Surfaced by pgrls (PERF001).

Fixes GIT-919

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

* docs(adding-a-trigger): fix RLS example cast placement for = any context

The `= any(...)` example put the ::text[] cast inside the sub-select, which
Postgres parses as a row-returning subquery and rejects at CREATE POLICY with
`operator does not exist: text = text[]`. Move the cast outside the sub-select
(matching the migration's canonical form) so the operand stays in array form,
and note why.

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-15 08:56:05 +02:00
Ruben Fiszel 88030d0f55 fix(tree-view): align file indentation with sibling folders (#10115)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 02:03:47 +02:00
Ruben Fiszel ba232544e7 feat(nativets): add Web Crypto support via deno_crypto (#10109)
The nativets in-process runtime (deno_core) exposed no Web Crypto API:
`crypto` was undefined, so scripts could not use `crypto.getRandomValues`,
`crypto.randomUUID`, or `crypto.subtle`, even though the bun runner provides
them. This closes that parity gap by registering the `deno_crypto` extension
and wiring the crypto globals onto `globalThis`.

- Pin `deno_crypto = "0.223.0"`, the sibling release of the already-pinned
  deno_core 0.352 / deno_web 0.240 stack (deps: deno_core ^0.352,
  deno_web ^0.240, deno_error =0.6.1), so the rest of the deno stack is
  untouched.
- Register `deno_crypto::init(None)` after `deno_web` in both the snapshot
  (build.rs) and the runtime (lib.rs) extension lists, keeping the snapshot a
  prefix of the runtime list. deno_crypto declares deps = [deno_webidl,
  deno_web], which the position satisfies.
- Import `ext:deno_crypto/00_crypto.js` in runtime.js and assign
  `crypto` / `Crypto` / `CryptoKey` / `SubtleCrypto` to `globalThis`.
- Add the `smoke_web_crypto` opt-in smoke test asserting the UUIDv4 shape of
  `randomUUID`, a non-zero `getRandomValues` fill, and the known
  SHA-256("abc") vector via `subtle.digest`.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 01:58:56 +02:00
Ruben Fiszel 4917f79935 fix(mcp): let MCP tokens call preview run tools (jobs:run scope) — Fixes GIT-920 (#10107)
* fix(mcp): let MCP tokens call preview run tools (jobs:run scope)

The MCP proxy mints an internal JWT scoped to exactly `scope_for_route`
for the endpoint it forwards to. For preview run routes
(`run/preview`, `run/preview_bundle`, `run/preview_flow`,
`run_wait_result/preview`, `run_wait_result/preview_flow`),
`determine_kind_from_route` matched the `SCRIPT_JOBS` prefix
`jobs/run_wait_result/p` (because "preview" starts with "p") and derived
`jobs:run:scripts`. But the preview handlers run arbitrary request-supplied
code with no deployed path and require the broad `jobs:run` scope, so
`jobs:run:scripts` was rejected with 403 "Required scope: jobs:run".

Preview/bundle routes now carry no runnable kind, so the derived scope is
the broad `jobs:run` the handlers expect. This also aligns the route-level
access check with the handler check for these routes.

Fixes GIT-920

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

* fix(mcp): anchor preview-route match to endpoint segment

Address CI review: `route_path.contains("preview")` also matched by-path
runs of a deployed runnable whose path contains "preview" (e.g.
`jobs/run_wait_result/p/f/team/preview_report`). Since determine_kind_from_route
also feeds check_route_access, such a route would derive the broad `jobs:run`
and reject a legitimately kind-scoped `jobs:run:scripts:*`/`jobs:run:flows:*`
token with 403.

Anchor the exception to the actual preview endpoints
(`jobs/run/preview*`, `jobs/run_wait_result/preview*`) so by-path runs keep
their kind. Add regression tests for preview-named by-path paths, and trim
the comments per AGENTS.md.

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-15 01:56:41 +02:00
Ruben Fiszel ba7f9c065f fix(nativets): apply parameter defaults for missing args instead of null (#10111)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 01:53:43 +02:00
Ruben Fiszel abbab4d423 security(docker): apt-get upgrade base OS in all runtime base stages (#10114)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 01:50:50 +02:00
Ruben Fiszel 4d0dee8d39 make Caddy bind tolerate an unset ADDRESS on caddy >= 2.9 (#10113)
Follow-up to GIT-903 / PR #10106. That PR migrated the caddy-l4 image to
mholt's native `layer4` Caddyfile support, which required bumping the
Caddy base image to 2.11.4.

End-to-end testing (running the image and proxying real traffic, not
just `caddy adapt`) revealed that caddy >= 2.9 changed how `bind` treats
an empty argument. The shipped Caddyfile has `bind {$ADDRESS}` inside the
`{$BASE_URL}` site, and docker-compose leaves ADDRESS unset -- the
default self-host case. On 2.11.4 the empty `bind` makes Caddy drop the
entire `{$BASE_URL}` site, so the container listens only on :25 (layer4)
and the :80 HTTP reverse proxy to windmill_server silently disappears.
config-only checks (adapt/validate/boot) pass, so only real traffic
surfaces it.

Fix in the Caddyfile rather than downgrading Caddy (which would
reintroduce known CVEs on an internet-facing proxy): default the bind to
all interfaces when ADDRESS is unset via `bind {$ADDRESS:0.0.0.0 ::}`.
When ADDRESS is set it is honored unchanged; when unset the site binds
IPv4 + IPv6, matching the pre-2.9 behavior.

Verified on the caddy:2.11.4 image with a mock windmill_server backend
(HTTP :8000 + layer4 echo :2525):
- ADDRESS unset  -> :80 and :25 both bind; HTTP and layer4 both proxy
- ADDRESS=0.0.0.0 -> same
- ADDRESS=127.0.0.1 -> HTTP site binds 127.0.0.1:80 (knob preserved)

Fixes GIT-903

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 01:49:48 +02:00
Ruben Fiszel 770ac2be9e fix(self-host): resolve caddy-l4 "unrecognized global option: layer4" error (#10106)
The self-hosted Caddy image relied on the abandoned
RussellLuo/caddy-ext/layer4 shim to provide the `layer4` Caddyfile
global option, alongside an old (May 2024) pin of mholt/caddy-l4 that
predated native Caddyfile support. This combination is fragile:

- If the image is ever built without the RussellLuo shim, the `layer4`
  global option disappears and Caddy fails with
  "unrecognized global option: layer4" — the reported bug.
- Bumping mholt/caddy-l4 to any version with native Caddyfile support
  makes both modules register `layer4`, panicking at startup with
  "global option 'layer4' already registered".

mholt/caddy-l4 now natively registers the `layer4` global option, so
drop the RussellLuo dependency entirely and switch the Caddyfile to the
native `route { proxy { upstream ... } }` syntax. The adapted layer4
JSON is byte-identical to the previous output, so runtime behavior is
unchanged.

Also bump the Caddy base image to 2.11.4 (required by current
caddy-l4) and add a path-filtered push trigger so the published
`:latest` image is rebuilt whenever the Caddy Dockerfile changes,
instead of only on manual dispatch (which is how `:latest` drifted out
of sync with the checked-in Caddyfile in the first place).

Fixes GIT-903

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 01:08:55 +02:00
Ruben Fiszel 65d6f477ab chore(main): release 1.758.0 (#10084)
* chore(main): release 1.758.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.758.0
2026-07-14 22:38:45 +02:00
Guilhem af3e3fe667 fix(ai-chat): size AI-created flow notes to fit their text (#10091)
* fix(ai-chat): size AI-created flow notes to fit their text

Free notes created via the flow AI chat omit `size` (the tool prompt tells
the model to let the editor size them). validateFlowNotes seeded a fixed
275x60 box, but free notes never grow to fit content, so multi-line markdown
overflowed the box. Estimate height from the text instead.

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

* fix(ai-chat): stack auto-placed flow notes by height to avoid overlap

Auto-placed free notes were staggered by a fixed index*84px step, but notes
can now be up to 600px tall, so consecutive generated notes overlapped. Track
a running y-cursor and advance it by each note's real height.

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

* fix(ai-chat): advance note stack cursor past preserved column notes

A round-tripped note keeps its existing auto-column geometry ({-375, y});
the stack cursor ignored it, so a newly added geometry-less note landed on
top. Preserved notes overlapping the auto-stack column now advance the cursor.

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

* chore(ai-chat): trim estimateFreeNoteSize comment per AGENTS.md

Keep only the non-obvious fixed-height renderer constraint; drop the
implementation narration.

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 22:33:55 +02:00
hugocasa 7ebfad382a feat(ai-agent): give tools a real description instead of the tool name (#10083)
* feat(ai-agent): use a real tool description instead of the tool name

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

* fix(ai-agent): render tool-name error full width and hoist it above the description

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

* fix(ai-agent): make tool description field hug its content so a single line is vertically centered

Add an optional minHeight param to the autosize action (default unchanged at 30px) and pass minHeight 0 for the tool description so an empty/one-line field no longer reserves the 30px floor and leaves dead space below the text.

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

* chore(ai-agent): regenerate OpenFlow-derived prompts, CLI guidance, and copilot zod schema for tool description

Fixes the check-freshness CI failure (system_prompts + skills.gen.ts) and makes the flow copilot's openFlow.json / openFlowZod.gen.ts aware of the new AgentTool.description field so AI-authored tools can set it.

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 22:33:16 +02:00
Guilhem f3cd5d9f7f fix(sessions): pending-draft debounce follow-ups (delete-cancel, keystroke de-transient, teardown count) (#10087)
* fix(sessions): cancel pending draft-prompt flush on delete

deleteSession removed the record from memory and IndexedDB but left the
debounced draft-prompt flush timer running; it would fire afterward and
persistTouched the deleted session back into IndexedDB, resurrecting a
draft deleted inside the 400ms window on the next reload. Clear the
per-session timer in deleteSession.

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

* fix(sessions): de-transient drafts on keystroke; count pending in workspace teardown

Two follow-ups to the pending-sessions feature (#10076), surfaced by codex review:

- setSessionDraftPrompt clears `transient` synchronously so a draft typed into
  is no longer treated as a reusable blank by createSession. Previously the flag
  only cleared 400ms later via the debounced flush, so pressing `+` right after
  typing reopened the same draft instead of spawning a second pending session.
  Only the IndexedDB write stays debounced.
- countSessionsForWorkspace counts on `workspace_id ?? pending_workspace_id`, so
  the archive/delete confirmation includes persisted unsent drafts, matching
  reconcileSessionsLifecycle which tears them down alongside committed sessions.

Adds regression tests that drive the real keystroke transition and the pending
draft count.

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

* fix(sessions): reuse-guard on draftPrompt, not a synchronous transient clear

Addresses codex-review P1 on #10087: the previous approach cleared `transient`
synchronously on a keystroke to stop createSession reusing a just-typed draft.
But `transient` also means "in-memory only, not yet in IndexedDB" — clearing it
before the 400ms flush left the draft in neither bucket, so a reconcile landing
inside the window (hydrateSessions rebuilds the list as in-memory-transients +
DB rows) dropped the unsaved draft and dangled currentSessionId.

Separate the two concepts instead: keep `transient` as pure persistence state
(the draft survives hydration), and define a reusable blank as
`transient && !draftPrompt`. createSession's reuse probe and its non-reuse drop
both key on isReusableBlank, so a typed-but-unflushed draft is neither reused nor
discarded, and setSessionDraftPrompt no longer touches `transient`.

Adds a regression test that interleaves a first-touch debounce with reconcile
and asserts the draft stays in memory (and currentSessionId intact); updates the
keystroke test to the real transient-preserving transition.

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

* fix(sessions): treat a typed-then-erased draft as touched, not a reusable blank

Addresses codex-review P2 on #10087. isReusableBlank used `!s.draftPrompt`, so a
draft typed into then erased back to '' (draftPrompt === '', flush still pending)
was classed as a reusable blank: pressing `+` within 400ms reused it, but after
the flush cleared `transient` the same `+` created a new session — inconsistent
across the debounce boundary, and in another family the non-reuse drop removed
the draft while its pending timer later persisted it back.

setSessionDraftPrompt only sets draftPrompt on a genuine edit (mount-time '' is a
no-op via the equality guard), so `draftPrompt === undefined` cleanly means
"never edited". Key isReusableBlank on that instead of falsiness.

Adds a type-then-erase-before-`+` regression test.

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

* docs(sessions): trim new comment blocks to AGENTS.md 4-line limit

Addresses codex-review P2 on #10087: condense the setSessionDraftPrompt,
countSessionsForWorkspace, and isReusableBlank comment blocks to <=4 lines per the
AGENTS.md rule. Comment-only, no logic change.

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

* docs(sessions): trim last two comment blocks to AGENTS.md 4-line limit

Follow-up to codex/pi P2 nits on #10087: condense the keystroke-transition test
comment (5→4 lines) and the countSessionsForWorkspace comment (→3 lines). All new
comment blocks in the PR are now ≤4 lines. Comment-only, no logic change.

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 22:29:34 +02:00
hugocasa 4edffeb84b fix(mcp): align script auto_kind filter with scripts list API (#10098)
The MCP `get_items` script filter used `auto_kind IS NULL`, which excluded
every script with a non-null `auto_kind` (pipeline, test, WAC, ...). These
are valid runnable scripts and should surface as MCP tools.

Switch to the deny-list `(auto_kind IS NULL OR auto_kind <> 'lib')`, matching
the scripts list API (windmill-api-scripts). Only library scripts (no main
function) are excluded; pipeline/test/WAC and any future auto_kind values are
included.

Fixes WIN-2190

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 21:00:27 +02:00
Guilhem 2c702efec0 fix(frontend): stop spurious raw-app reload that 404s on "Start without AI" (#10099)
* fix(frontend): stop spurious raw-app reload that 404s on "Start without AI"

Creating a new raw app and clicking "Start without AI" surfaced an "App not
found" toast. The page's load effect re-ran loadApp() mid-bootstrap and fetched
the draft via getAppByPath before the first autosave POST had landed → 404.

Root cause: the effect used the legacy run() from svelte/legacy without
untrack, so loadApp()'s synchronous reactive read of the draft-hint SvelteMap
(getLocalDraftHint via shouldSeedNewDraft, added in #10044) subscribed the
effect. The first autosave optimistically flips that hint (#9351) before its
debounced POST, re-firing the effect → spurious loadApp() → getAppByPath on a
not-yet-persisted draft.

Convert the block to $effect + untrack so it depends only on page.params.path /
$workspaceStore, matching the sibling apps/edit and flows/edit routes. Autosave
and draft persistence are unchanged; only the phantom reload is removed.

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

* chore(frontend): tighten untrack invariant comment to ≤4 lines

Per AGENTS.md comment policy (Codex review nit).

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 19:02:22 +02:00
hugocasa cfc3f292ad fix(apps): allow setting sandbox isolation and public access before first deploy (#10085)
* fix(apps): allow enabling sandbox isolation before first deploy

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

* fix(apps): allow setting public access mode before first deploy

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 17:19:42 +02:00
hugocasa 98e6cca75d feat(cli): add --tag override to script and flow run/preview (#10079)
* feat: custom tags on CLI runs, show previews in default runs view

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

* revert: don't include previews in default runs view

Deferring the runs-view UX change; keeping only the CLI --tag work.

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

* fix(cli): forward --tag for codebase/bundle script previews

The bundled-preview branch posts a multipart payload to
/jobs/run/preview_bundle; --tag was only wired into the non-bundled
runScriptPreview call, so codebase previews silently used the default
tag. Include tag in the preview payload (backend reads preview.tag).

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 17:18:42 +02:00
Guilhem 32f32d9a29 feat(ai-chat): port flow-group and sticky-note instructions to global chat (#10090)
* feat(ai-chat): port flow-group and sticky-note instructions to global mode

Global-mode AI chat inherited only the bare FlowGroup schema and had no
sticky-note support, so it never proactively segmented flows into groups
and could not author flow-wide notes. Flow mode carried this guidance
inline in its own prompt and set_flow_json tool.

Bring global mode to parity:
- Enrich write_flow's `groups` description (color palette + fields) and add
  a `notes` field mirroring flow mode's set_flow_json.
- Thread `notes` through editableFlowToDraftValue and the write_flow handler
  so notes reach FlowValue.value and survive the deploy round-trip. Reads and
  patch_flow_json already carried notes via the shared editableFlowJson helpers.
- Expand getFlowInstructions with the groups/notes organizing guidance
  (strongly-recommended proactive grouping, color palette, when-to-use-which)
  and mention notes in the write/read/compact-view/structural-edit bullets.

Add a write_flow -> read_workspace_item notes round-trip test.

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

* refactor(ai-chat): trim write_flow groups/notes schema descriptions

The write_flow tool schema is re-sent every chat loop iteration, so the
verbose groups/notes descriptions were a per-iteration token tax that
duplicated the on-demand getFlowInstructions() prose. Trim the .describe()
calls to the correctness-critical bits (color palette, type "free", null
semantics) and point to get_instructions for the full field reference,
which getFlowInstructions() already carries.

Addresses CI review feedback (Claude + Pi).

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 17:18:01 +02:00
Ruben Fiszel 89bb63cff5 ci: drop debuginfo in backend integration tests to prevent runner OOM (#10088)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 16:58:24 +02:00
Guilhem eff9076e91 fix(sessions): reopen script test panel when preview goes full screen (#10082)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 15:52:20 +02:00
Alexander Petric f2869d8c1a fix(apps): load themes when selecting the Resources → Theme tab (#10086)
The Resources page dispatches per-tab data loads from the Tabs
`on:selected` handler and `reload()`, but both only handled `cache`
and `states` — selecting the Theme tab never called `loadTheme()`, so
`themeResources` stayed undefined and the tab rendered empty even
though app themes existed. The reload `$effect` reads `tab` inside
`untrack`, so it didn't re-fire on tab change either (only a filter or
workspace change did, which is why typing in the filter "fixed" it).

Add the missing `theme` branch in both places.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 15:17:26 +02:00
Ruben Fiszel 15391f6399 perf(runs): index-bound batch re-run selection with a lossless completed_at bound (WIN-2168) (#10074)
"Re-run all jobs matching filters" selects completed jobs via list_filtered_uuids
windowed by started_before/started_after (the timeframe). v2_job_completed has no
index on started_at (only completed_at), so that filter alone forces a
workspace-wide seq scan — a query observed at ~48s on a large instance.

started_at >= minTs implies completed_at >= minTs (a job completes at/after it
starts), so adding completedAfter = minTs is a lossless bound: it drops no row the
started_at window keeps, but lets the (workspace_id, completed_at DESC) index start
the scan at the window's lower edge instead of scanning the whole table. The
selected cohort is unchanged (started_at stays the exact filter); this is purely a
plan improvement. EXPLAIN: seq scan -> completed_at index scan.

Not completedBefore: a job can start in-window but finish after maxTs, and bounding
completed_at above would drop it. Scoped to re-run; batch cancel (v2_job_queue,
small) is untouched.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 15:15:48 +02:00
Guilhem bfcec7e8ac feat(sessions): support many pending sessions persisted in IndexedDB (#10076)
* feat(sessions): support many pending sessions persisted in IndexedDB

Allow several unsent AI sessions to be set up in parallel. Split the
transient flag into "in-memory, not yet persisted" (unsent is derived
from workspace_id), persist a pending session to IndexedDB on first
touch with its own draftPrompt, show pending sessions in the sidebar
under the family filter, and reconcile them by pending_workspace_id.
The + button reuses the untouched draft in the active family so idle
clicks don't pile blank entries; touching one spawns a fresh blank.

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

* feat(sessions): focus composer when + reuses the untouched draft

When there are no pending changes, `+` reuses the active family's untouched
draft instead of creating a new session (unchanged). But when the reused draft
is the one already on screen, currentSessionId doesn't change, so nothing
navigated and the click gave no feedback. Bump a composerFocusRequest nonce in
the reuse branch and have SessionWrapper's focus effect depend on it, so the
composer re-focuses and the user can type right away.

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

* fix(sessions): per-session debounce for draft prompt flush

A single module-level flush timer let a keystroke in one pending draft
cancel a sibling draft's pending first-touch flush, so the earlier draft
was never written and its typed prompt vanished on reload. Key the
debounce per session so parallel drafts persist independently. Also
collapse the touch rationale repeated across the preview-tab/collapse/size
setters onto persistTouched.

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 13:21:06 +02:00
Ruben Fiszel 1ffe5a1075 chore(main): release 1.757.0 (#10080)
* chore(main): release 1.757.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.757.0
2026-07-14 12:47:53 +02:00
hugocasa 851e30914e feat(saml): add ALLOW_PRIVATE_SAML_METADATA_URLS SSRF bypass (#10077)
* feat(saml): add ALLOW_PRIVATE_SAML_METADATA_URLS SSRF bypass

Introduce the ALLOW_PRIVATE_SAML_METADATA_URLS env var and its
allow_private_saml_metadata_urls() helper, mirroring the existing
ALLOW_PRIVATE_MCP_SERVER_URLS opt-out. This lets self-hosted deployments
with internal SAML IdPs (private IPs, no public DNS) skip the metadata-URL
SSRF check that otherwise blocks server startup.

The companion EE change (saml_ee.rs) consumes the helper to gate the
validate_url_for_ssrf() call and additionally treats a cleared
(empty/whitespace-only) SAML_METADATA setting as no SAML configured.

Fixes WIN-2169

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

* feat(saml): surface opt-in hint and record SSRF control in threat model

Add saml_ssrf_error_message() so private-IdP metadata URL rejections point
to ALLOW_PRIVATE_SAML_METADATA_URLS (mirroring the MCP helper), with a unit
test. Record the new SSRF opt-in under T2 in THREAT_MODEL.md, and bump the
EE ref for the companion saml_ee.rs change.

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

* refactor(saml): add validate_saml_metadata_url with opt-in unit tests

Factor the SAML metadata SSRF gating into validate_saml_metadata_url()
(mirroring validate_mcp_server_url) so the private-URL opt-in branch is
unit-tested at the ssrf layer: blocks private by default, allows on
true/1, and keeps scheme/host syntax guards when the opt-in is on. Bump
the EE ref for the companion saml_ee.rs change.

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

* chore: update ee-repo-ref to 394ad23242de429aef4074cc1dc28867dac95870

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

Previous ee-repo-ref: 86da208c5aef2570568e18c7ab98f4d58adeec18

New ee-repo-ref: 394ad23242de429aef4074cc1dc28867dac95870

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-14 12:24:22 +02:00
hugocasa 46f07ab032 docs: mandate local-review-codex alongside local-review before PRs (#10078)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 12:21:08 +02:00