* feat: auto-build binaries to object storage on deployment
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: queue the auto-build from pre-locked deploys and off the lock slot
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: materialize companion modules before a deploy-time build
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep a build job from stamping lock_error_logs on a healthy script
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: de-flake test_flow_lock_all and surface the lock error it hides
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: trim drafting history from the flow-lock fixture comments
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: stop a binary build from restarting dedicated workers
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the build-job marker off the agent wire and out of user args
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: stop websockets resurrecting a reclaimed dev server
* docs: condense the websocket invariant comment
* test: stub fetch suite-wide so waking cannot hit a real dev server
* fix: let websockets join an in-flight start
* fix: close SSRF bypasses in git URL validation (DNS + redirects)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: name the remedy when a git probe stops at a redirect
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: retry the .git form when a probe stops at a same-host redirect
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the .git retry on the validated host for pathless URLs
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: add dev server supervisor and dev-only polling dormancy
* fix: address review findings in dev supervisor
* fix: support https mode and bound the idle reaper in dev supervisor
* fix: persist dormancy install guard and hold the reaper during startup
* chore: run worktree frontends under the dev supervisor
* fix: keep app websockets working and reap children on sighup
* feat: add EXIT_AFTER_N_JOBS worker mode for environment cleanup
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review findings on the EXIT_AFTER_N_JOBS worker mode
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address round-2 review findings on EXIT_AFTER_N_JOBS
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address round-3 review findings on EXIT_AFTER_N_JOBS
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: bound WORKER_SUFFIX length and document the same-worker drain
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: validate the assembled worker name length
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: bound go compilation memory with GOMEMLIMIT
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: bound the whole go build tree, not each toolchain process
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the go build memlimit and parallelism atomic
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: log the go limits actually installed and stop serializing small workers
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: make go build parallelism authoritative over persisted GOFLAGS
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: canonicalize the go build -p value and floor the module-step budget
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: parse GOMAXPROCS for -p the way the go runtime does
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: read GOMAXPROCS with go's own grammar and report limits neutrally
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: derive go build parallelism from the cgroup quota over its own period
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep go's minimum build parallelism under sub-CPU quotas
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the windows 1CU cap out of go's two-compiler floor
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: record that a worker runs one job at a time
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: scope the one-job-at-a-time rule away from native workers
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: home search matches each term instead of the whole query verbatim
* docs: state the search term cap and drop unreachable test cases
* fix: treat a term-less search as no filter and trim the comment
* fix: a term-less search matches nothing instead of the whole page
* feat: match the homepage fuzzy search exactly in the runnables endpoint
* docs: say apostrophes stay in terms; test summary-less and draft rows
* docs: separate an empty search from one holding no terms
* docs: state that terms split on ASCII alphanumerics only
* fix: escape and validate custom env var names in the nativets prologue
Custom workspace environment variable names were spliced verbatim into the
generated NativeTS/Bun JS prologue (both the `const {name}` binding and the
`process.env['{name}']` assignment), while only the value was escaped. A
non-identifier name could therefore alter the generated program.
- Add `escape_js_single_quoted` / `is_valid_js_identifier` helpers.
- worker.rs and bun_executor.rs: escape the name as a string literal, and only
emit the `const {name}` binding for valid identifiers.
- set_environment_variable: reject non-identifier names on write (deletion stays
unrestricted so existing rows remain removable).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: address review — reserved-word const gate, grandfathered-name editability
- Gate the `const {name}` prologue binding on `can_bind_as_prologue_const`, which
additionally excludes JS reserved words and the prologue's own bindings
(`process`, `BASE_URL`, `BASE_INTERNAL_URL`); such names would otherwise emit a
SyntaxError that breaks every NativeTS run. They are still exposed via
`process.env['{name}']`.
- set_environment_variable: only enforce the identifier check for names that don't
already exist, so editing the value of a pre-existing non-identifier name (the
edit UI resubmits the name) isn't rejected with no in-product fix.
- Document the name constraint on the endpoint in openapi.yaml.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: exclude eval/arguments from const gate; skip existence query on valid names
- Strict-mode ES modules forbid `eval` and `arguments` as binding names, so add
them to the non-bindable set — otherwise an env var named `eval`/`arguments`
emits `const eval = ...`, a SyntaxError that breaks every NativeTS run.
- set_environment_variable: run the existence check only when the name isn't a
valid identifier, so the common (valid-name) path skips the extra query; trim
the rationale comment.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: allow `async` as a prologue const binding; note reserved-bindings coupling
`async` is a contextual keyword, not a reserved word — `const async = ...` is
valid, so it needn't be excluded from the const binding. Also cross-reference the
prologue head from PROLOGUE_RESERVED_BINDINGS so the two stay in sync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix: authorize GET /concurrency_groups/{job_id}/key per job
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: answer 404 for an inaccessible and an unknown job alike
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(sessions): persist artifact version selection in preview tabs
The artifact viewer's version pin was component-local state, so picking an
older version from the history dropdown was lost on reload. It now rides on
the preview tab's URL (`artifact:<id>?v=<n>#<name>`), which is persisted with
the tab, so a reload lands the reader back on the version they were reading.
Omitting a version means "leave the reader where they are", not "show the
latest". Every artifact tool re-opens the document it just wrote, so an
omitted version that cleared the pin would yank a reader out of the version
they chose on every single edit. That rule lives in keptVersion(), which
targetUrl() applies to every path that re-points a tab, so open() and
navigate() cannot disagree about it — the breadcrumb picker opens highlighting
the artifact the active tab already shows, and re-picking it must not double
as a reset to latest. A pin belongs to a (tab, artifact) pair, so a tab
re-pointed at a different document carries nothing over, and a new tab starts
unpinned. Moving off a pin is the reader's own action, through the version
dropdown, "Back to latest", or the new pinArtifactVersion(). Since the pin is
part of the tab model, get_preview_status now reports it, so the assistant can
tell that the reader is not looking at what it just wrote.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(sessions): bound a stamped artifact version to a safe integer
Number.isInteger(1e21) is true, but interpolating it yields `?v=1e+21` while
parseArtifactRoute matches digits only, so artifactUrl could stamp a url that
reads back as null — the one outcome the guard exists to prevent, and one that
would persist with the tab. Safe integers always interpolate in decimal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(artifacts): tell a failed version read apart from a missing version
getArtifactVersion swallowed a rejected read and returned undefined, so a
transient IndexedDB failure was indistinguishable from a pruned snapshot. Both
its callers act on that distinction, and both acted wrongly: the artifact
viewer clears the reader's pinned version on absence — now that the pin is
persisted with the tab, clearing it destroys it — and read_artifact tells the
model the version is gone and to call list_artifact_versions.
It now rejects instead. The store still answers for the current version, which
it holds in memory and can serve without the DB; anything older propagates, the
viewer keeps the pin and leaves the document on screen, and read_artifact
reports a read it could not make rather than a version that does not exist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix: clear orphaned usr_to_group rows on service account creation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: pin service account creation over orphaned usr_to_group rows
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 60c20e686cead73ff075512b15c6e2d6232beca6
This commit updates the EE repository reference after PR #723 was merged in windmill-ee-private.
Previous ee-repo-ref: 5c2c553f960abcd7988fdac8830dd36c066160ad
New ee-repo-ref: 60c20e686cead73ff075512b15c6e2d6232beca6
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix(frontend): use the Password component on the login and reset-password forms
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): submit auth forms once per Enter keypress
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): conceal revealed password before submitting auth forms
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: stop the AI chat destroying secret variables on edit
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: clear stale staged secret values and state the draft-staging rule
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: condense the pending-secret invariant to its field
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: refuse empty and oauth-managed secret values, keep drawer-staged ones in the draft
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: resolve a variable deploy's secret from one draft snapshot
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: make the variable draft the single source of a staged secret
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: drop stale in-memory secret invariants from comments and the eval
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: stop null account/expires_at leaking into variable drafts and diffs
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: report when a variable deploy leaves the secret value unchanged
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: scope the variable-value readability claims to the chat
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: correct the secret-draft invariant in the diff masking comment
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: record why a non-secret value is resent on a partial update
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: stop "Load secret value" discarding a staged secret
The audit-logged load writes the deployed secret into the draft row the
variable drawer shares with the AI chat, so offering it while that row
already stages a value silently replaces it — and the deploy that follows
carries the old value with no sign the staged one was lost.
The gate that hid the action already existed but keyed on
`isEncryptedDraftValue`, which only holds once a draft has round-tripped
through the server. A value staged in the same tab is still plaintext, so
it slipped through. Key on "anything staged" instead; clearing stays
explicit via Reset.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: extend the variable draft's empty-value sentinel past secrets
Two gaps in the chat's variable write path, both from treating "the draft
cannot carry this value" as meaning only "the value is secret".
`variableToDraftState` drops the value of an OAuth-managed variable so a
refreshed live token is never pinned into a draft, leaving '' behind. The
deploy body resent that '' verbatim for a non-secret one, wiping the token
the refresh flow owns. The sentinel now covers every value the draft is not
allowed to hold, which also removes the divergence from
`VariableEditor.save` and the shared deployer.
Making a variable secret when it holds no value produced a secret draft
staging '', a deploy body with no `value`, and the backend's "cannot change
is_secret without updating value too" — the sibling create path already
answers that case with guidance, so answer it here too.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: gate the Secret toggle's secret load on the staged value too
The toggle calls `onLoadSecret` on every change so an is_secret flip has a
value to send, but that load overwrites the shared draft row — the same
discard the button gate just closed, reached by a different control.
It now loads only when the row stages nothing, which is exactly when the
flip needs a value fetched. With a value already staged there is one to
send, and it is the one the user or the chat put there.
Blocking the load costs the side effect that used to mask a worse bug: for
a deployed variable, the load replaced an `$encrypted:` marker with real
plaintext before save. Without it, un-securing a marker would store the
marker string as the value, since the deploy endpoints only decrypt it while
is_secret stays true. So the toggle is disabled outright while a marker is
staged — Reset first. That closes the marker case for draft-only variables
as well, where no load could ever have masked it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
create_schedule opened the RLS transaction (user_db.begin) first, then ran
reads that deliberately use the non-RLS `db` pool — fork-ness and
permissioned_as/email resolution — while holding it. Acquiring a second pooled
connection while the tx holds one self-deadlocks on a single-connection pool
(embedded Postgres, PgBouncer statement mode, any max_connections=1 setup): the
read blocks on the sqlx acquire timeout, then errors.
Move those reads (and the ScheduleType::from_str validation) above
user_db.begin(). They don't depend on the tx and bypass RLS by design, so the
result is semantically identical; the RLS transaction is simply opened later and
held for less time. Same class of fix as #9970 (migration bootstrap on the
migrator's held connection).
Note: sibling paths keep the same latent pattern on branches this change does
not touch (push_scheduled_job reads the pool under the tx for flow schedules;
edit_schedule/set_enabled for cross-user permissioned_as) — a possible follow-up.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* test: wait for the app dependency job before pulling in repro_diffname
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: reuse waitForDeploymentJobs and assert pulled lock files
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: condense the dependency-job wait comment
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: expose every runs filter on the open_page chat tool
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: reject runs filters the page would silently ignore
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: normalize runs list filters and refuse combinations the page drops
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: validate the full folder-name contract and pin evals to one call
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: refuse queue statuses the concurrency view cannot filter on
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: avoid content shift on home page load and in the script editor logs pane
The tutorial banner rendered by default and was removed once an API round-trip
resolved that it should not show, jumping everything below it up by 58px on
every home page load. It now caches the last resolved state in localStorage and
paints that first, so the first frame already matches what the sync concludes; a
device with nothing cached stays hidden until the sync answers.
The logs header spinner was an unsized lucide icon (24px) where the settled
state renders a 12px Timer, so the row grew 7px while a job was queued and
shrank back when it started, shoving the log body down and up again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the tutorial banner hidden when dismissed mid-sync
The banner is interactive while the initial tutorial-progress request is still
in flight, so a dismiss or a skip can land before the sync resolves. The
continuation then overwrote the user's choice and brought the banner back.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: pin the result placeholder row height across the spinner swap
Sizing the spinner to the font size still left it 6px short of the text-sm line
box it replaces, so the row contracted instead of growing. Pin the height on the
container so it holds in both states and tracks the root font size.
Also assign state before persisting it, and collapse the duplicated rationale
above the banner cache.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: stop the test panel splitpanes resting one header too tall
The panes carried `!max-h-[calc(100%-{...}px)]`, but the arbitrary value is
built by string interpolation so Tailwind never emitted a rule for it: the
class was inert and the computed max-height was `none`. The panes then took
their 100% height, ignoring the header row above them, and overflowed the
column by exactly the header. Flex only applied the shrink transiently, so a
reflow during a run snapped the whole logs & result region up ~12px and back.
min-h-0 lets flex size the panes to the space that is actually left, which is
what the clamp was reaching for and is correct for the debug and bottom layouts
too, without their hardcoded 83/43/0 pixel guesses.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: bound postgres result collection so it cannot OOM the worker
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: render the sql result limit exactly so the error can be set verbatim
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: point the fraction rationale at the renderer that still emits them
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* perf: stop re-parsing every collected row to rebuild it as a RawValue
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* style: drop a dangling doc line and an unrelated rustfmt reflow
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: bound duckdb result collection so an oversized result cannot OOM the worker
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the duckdb cap a worker-survival limit rather than a cloud product one
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: refuse an oversized blob before it expands to one json value per byte
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: share one expansion budget across a row's values, nested ones included
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: bound the row's own serialization so escaping cannot outgrow the budget
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: charge a json column before parsing it into a value tree
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: trim the json budget rationale and name what the budget does not cover
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: gate the sql result size limit on the duckdb feature
Its only consumer is the duckdb executor, so the minimal build compiled it
as dead code and failed under -D warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: bound how much disk a single duckdb job can spill
* fix: name the env var and correct duckdb's unreachable spill-cap advice
* fix: do not blame an unset env var for duckdb's default spill cap
* style: keep the duckdb spill-cap invariant comments within four lines
* docs: size the duckdb spill cap against the disk cloud pods actually use
* fix: tell MCP clients which tool parameters may be omitted
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: make the mcp property-key rename testable and shorten the hint
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the mcp omission hint from calling flow inputs optional
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: skip the mcp omission hint on a parameterless tool
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: design for where the npm proxy keeps cached registry content
* feat(npm-proxy): keep package files on disk and in the object store
* fix(npm-proxy): degrade when the cache is unwritable, stream and bound it
* fix(npm-proxy): keep the happy path off the heap and isolate pull scratch
* fix(npm-proxy): bound the upload, verify pulled trees, keep oversized manifests
* fix(npm-proxy): protect live scratch, bound uploads by parts, refuse traversals
* fix: let the blocking unpack own the scratch it writes into
* fix: replace a cache directory that is not a package instead of deferring to it
* fix: evict by moving a package off the live path, not by deleting it in place
* fix: leave a package the sweep cannot move rather than deleting it in place
* fix: take one registry snapshot through a cache miss
* fix: stamp a pulled package as used so the sweep does not evict it first
* fix: explain duckdb failures caused by job isolation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: apply the isolation policy to the schema-sync pre-pass
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: bump ee ref for the out-of-memory hint wording
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: bump the bundled DuckDB engine to 1.5.5
The 1.5.5 duckdb crate no longer hands back a 96-bit `rust_decimal`, so a
DECIMAL wider than that renders instead of panicking inside an `extern "C"`
frame — which, being unable to unwind, aborted the whole worker process and
left the job running as a zombie. `SELECT
'1234567890123456789012345678.9012345678'::DECIMAL(38, 10)` was enough.
Adapting to the crate's API: `Value` is now `#[non_exhaustive]` and gained
`UHugeInt` and `Geometry`, and `rust_decimal` became an optional feature that
the `decimal`/`numeric` argument path still needs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review findings on the duckdb bump
Run the FFI crate's own tests in CI: it is excluded from the workspace, so the
`cargo test --all` in backend-test never reached them and the new guard against
the worker-aborting DECIMAL would not have run. build_dev.sh now honors a
caller-pinned CARGO_TARGET_DIR so the test build reuses that compile instead of
building the bundled engine a second time.
Also pin UHUGEINT rendering, and correct the rust_decimal rationale —
`Decimal::new` is public without the feature, so the reason is that the feature
reproduces the exact binding the crate used to derive, not that nothing else can.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review nits on the duckdb bump
Name the unsupported DuckDB type rather than dumping the value, which may be
arbitrarily large or hold data that does not belong in an error message, and
say which column it came from.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: pin the ee ref to the narrowed duckdb extension allowlist
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: keep duckdb spilling behind the local-filesystem fence
* chore: repin the duckdb fork after adding the reset-test exclusion
* docs: stop claiming the duckdb patch has been filed upstream
* docs: point the backend duckdb bullet at the fork's rationale
* fix: place lock_temp_directory so no existing struct member moves
* fix: skip the extension-load guard when the repo is unreachable
* refactor: trim the fork comments and fail the extension guard in CI
* chore: repin the duckdb fork onto upstream duckdb-rs main
* fix: keep the engine patch applying on a CRLF checkout
* docs: link the upstream issue tracking the underlying problem
* chore: repin the duckdb fork onto the patch as filed upstream
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 88568d11162ffa11723e7955e613224bab4f0568
This commit updates the EE repository reference after PR #720 was merged in windmill-ee-private.
Previous ee-repo-ref: 22f075c1164d9dd5a3ba92d682905aabd071d273
New ee-repo-ref: 88568d11162ffa11723e7955e613224bab4f0568
Automated by sync-ee-ref workflow.
* chore: repin the duckdb fork onto the cmake/fmt build fix
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: pin the immutability half of lock_temp_directory
The spill test proves the exemption works; nothing proved the lock that makes
it sound. A rebase could drop the refusals and leave every other tripwire green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix(cli): route fileset children to their parent resource on sync push
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): scope fileset pointer validation to sync pushes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): error on script push of file/fileset resource content files
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): enforce server-canonical fileset pointers and fail fast before apply
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): resolve ws-specific fileset metadata and validate pointers before dry-run
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): prefer workspace-specific fileset metadata over base file
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(cli): make fileset metadata lookup assertions platform-separator safe
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): stop dropping fileset children whose names look like typed metadata
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): exempt fileset children from the current-workspace classifier too
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(frontend): declutter the workspace picker and workspace creation
* fix(frontend): fail open when the auto-invite domain check errors
* fix(frontend): fail closed when the auto-invite domain check errors
* feat(frontend): present prod and dev as sibling choices on the workspace card
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(ata): prefer the npm proxy when the instance configures a registry
* fix(npm-proxy): cap tarball extraction and stop pinning a failed config probe
* fix(npm-proxy): keep large packages cacheable by using a single shard
* fix(npm-proxy): cache the archive so a large package is served, not refused
* fix(npm-proxy): read archives off the runtime, keeping only what types need
* fix(npm-proxy): charge a retained entry for what it allocates, not its bytes
* fix(npm-proxy): size retention for real packages and read the manifest back
* fix(npm-proxy): charge path bytes and pin the manifest read-back
* fix(npm-proxy): stop retaining past the budget instead of refusing the package
* fix(parser-py): keep first param when def main( line has trailing comment
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: bump windmill-parser-wasm-py to 1.782.0
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(ata): fall back to the npm proxy when the CDN request fails outright
* fix(ata): surface proxy failures and guard the body read too
* docs(ata): state the proxy catch's constraint, not its history
* fix(ata): log a failed proxy d.ts fetch, which callers discard
* fix: carry the validated token endpoint with MCP OAuth credentials
get_or_refresh_mcp_client already resolved and checked the token endpoint on
both its cached and freshly-registered paths, then dropped the result. Keeping
it on McpClientCredentials lets the callers that post the client_secret there
connect to the address that was checked, and removes a second lookup they were
each doing on their own.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: hand out the token URL with the client pinned to it
Makes the pin unrepresentable-if-wrong rather than documented: the validated
target is private and reachable only through token_request, which returns the
URL together with the client pinned to the address it was checked against, so
a caller cannot pin one host and post to another.
Adds the test that was missing under the whole guard: that the pinned client
really does connect to the pinned address instead of resolving the host. The
accept loop is bounded, so a pin that stops working fails in seconds.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the token endpoint private behind token_request
Leaving the URL public still allowed posting the client_secret to it on an
unpinned client, so the invariant was only documented. Both the URL and its
validated target are now private and reachable together, and the pinning test
resets the accepted socket to blocking so it does not read empty on macOS.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: drop the non-blocking reset from the pinning test
Linux hands back a blocking socket from accept regardless of the listener's
flag, and no runner here builds this crate for a platform that does otherwise.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to f8d523195e40fd1d740595dcab6ce5cdc1bdbf09
This commit updates the EE repository reference after PR #718 was merged in windmill-ee-private.
Previous ee-repo-ref: 729df45314c6f2168b44eddb6edea401b0495d6d
New ee-repo-ref: f8d523195e40fd1d740595dcab6ce5cdc1bdbf09
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* feat(raw-apps): route in-browser npm installs through the npm proxy
* fix(npm-proxy): follow npm range semantics and cache packuments
* fix(npm-proxy): bound the packument cache by bytes and stream tarballs
* fix(npm-proxy): keep a v-prefixed pin exact and read the tarball once
* chore(raw-apps): bump the ui_builder pin to the npm-proxy installer
* feat(triggers): nested any_of / all_of filter groups
A trigger filter entry can now be a group — `{"any_of": [...]}` or
`{"all_of": [...]}` — nesting further entries, so criteria like
`A AND B AND (C OR D)` are expressible. Existing flat `{key, value}` lists keep
their meaning, combined by the trigger's `filter_logic` as before.
Filters are compiled once per connection: the set of top-level keys the whole
tree references is collected up front, so a message is parsed in a single
streaming pass that captures only those keys, instead of one full pass per leaf
filter as before. Filters that fail to parse are now logged rather than dropped
silently, since a nested group is easier to mistype than a flat entry.
The editor gains "Add group", rendering groups recursively with their own
AND/OR selector; Kafka and WebSocket triggers share it.
Fixes WIN-2345
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(triggers): drop empty filter groups instead of evaluating them
A group with no criterion cannot evaluate to a constant: true makes an `or`
filter list accept every message, false mutes an `and` list. Two clicks in the
editor ("Add group", save) produced one. Drop it when compiling so its siblings
stay in force, and reject at save time the filters the listener would otherwise
drop silently.
Also restore the item shape of `$ref`-typed arrays in the generated agent
schemas: the extractor only resolved refs at the property level, so moving
`filters.items` to a shared schema flattened it to a bare object. Resolving them
inside `items` too also recovers the shapes `initial_messages` and the MQTT
`topics` had already lost.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(triggers): name the offending entry when a nested filter is invalid
Serde's untagged error only reports that the outermost entry matched no
variant, whatever depth is actually wrong, which defeats the point of
validating a group at save time. Walk the tree instead and report the path.
Normalize the WebSocket editor's filters to [] on load, as the Kafka editor
does, so the list component can rely on an array.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(triggers): key filter rows by node so deletion keeps values aligned
The value editor seeds itself from `code` once, so an index-keyed row reused
for a different filter kept showing the deleted row's value.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: bump ee-repo-ref after merging main
The merge pulled OSS code that needs EE symbols newer than the companion
branch's base, so the companion was merged with EE main too.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* perf(triggers): keep filter short-circuiting from materializing unread fields
The single-pass scan deserialized every referenced key before the boolean tree
ran, so an AND whose first leaf rejects the message still allocated the large
objects the later leaves name — the shape this feature exists for. Borrow the
wanted keys as raw slices during the scan and parse a field only when
evaluation actually reaches it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(triggers): none_of filter group
Negation of a nested group, so a trigger can exclude what it must not react to
without inverting every other criterion. A key the message does not carry
satisfies it: there is nothing there to match.
Only groups can negate — the root's operator is the trigger's filter_logic
column, which has no value for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(triggers): address a nested field with a dotted path
`{path: "a.b.c", value: v}` alongside the existing `{key, value}`, so the common
case reads the way people write it instead of nesting the shape into the value.
A separate field rather than dots in `key`, which already means the top-level
field spelled that way — overloading it would resettle what existing triggers
over flattened payloads match.
Paths address objects only for now: a path through an array does not match
rather than guessing an element, and array containment stays on the value side.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(triggers): mention none_of in the filter_logic description
Plus a test for the empty-path-segment rejection, which had none.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 78859aab0c6e78283ec8d2b37e8c410963afdc83
This commit updates the EE repository reference after PR #722 was merged in windmill-ee-private.
Previous ee-repo-ref: 0e42ba72ccc38a6b0a380f58afe0db36d284f4c9
New ee-repo-ref: 78859aab0c6e78283ec8d2b37e8c410963afdc83
Automated by sync-ee-ref workflow.
* fix(triggers): reject a criterion naming both key and path
The untagged enum takes such an entry as a `key` criterion and drops the
`path`, which is the silent-ignore the save-time validation exists to prevent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor(triggers): drop the label next to the key/path toggle
The toggle already shows which one is selected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(triggers): reject an entry that combines a criterion with a group
Generalizes the key+path fix: the untagged enum settles a half-and-half entry
on the first variant that fits and ignores the rest, so a criterion carrying a
group key lost the whole subtree without a word.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: bump the bundled DuckDB engine to 1.5.5
The 1.5.5 duckdb crate no longer hands back a 96-bit `rust_decimal`, so a
DECIMAL wider than that renders instead of panicking inside an `extern "C"`
frame — which, being unable to unwind, aborted the whole worker process and
left the job running as a zombie. `SELECT
'1234567890123456789012345678.9012345678'::DECIMAL(38, 10)` was enough.
Adapting to the crate's API: `Value` is now `#[non_exhaustive]` and gained
`UHugeInt` and `Geometry`, and `rust_decimal` became an optional feature that
the `decimal`/`numeric` argument path still needs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review findings on the duckdb bump
Run the FFI crate's own tests in CI: it is excluded from the workspace, so the
`cargo test --all` in backend-test never reached them and the new guard against
the worker-aborting DECIMAL would not have run. build_dev.sh now honors a
caller-pinned CARGO_TARGET_DIR so the test build reuses that compile instead of
building the bundled engine a second time.
Also pin UHUGEINT rendering, and correct the rust_decimal rationale —
`Decimal::new` is public without the feature, so the reason is that the feature
reproduces the exact binding the crate used to derive, not that nothing else can.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review nits on the duckdb bump
Name the unsupported DuckDB type rather than dumping the value, which may be
arbitrarily large or hold data that does not belong in an error message, and
say which column it came from.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: pin the ee ref to the narrowed duckdb extension allowlist
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: pin the ee ref to the verified duckdb extension allowlist
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: pin the ee ref to the allowlist regression test
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 04dd9c5c352f04995cd0470400a877261f956561
This commit updates the EE repository reference after PR #716 was merged in windmill-ee-private.
Previous ee-repo-ref: fe7eb440a5bbae37774d3a96b69ab5c46c0b8936
New ee-repo-ref: 04dd9c5c352f04995cd0470400a877261f956561
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix(sdk): unbreak the JSR publish of the typescript client
`Sql` is `export type Sql = string`, but build.jsr.sh re-exported it as a
value, so `deno publish` fails type-checking with TS1205 under
isolatedModules. Every `v*` tag since has published nothing to JSR.
The npm build never noticed because it lists the same symbol as `type Sql`;
the two scripts keep separate copies of the export list.
Record both JSR-only constraints next to the list, since neither shows up
until a release tag runs publish.jsr.sh.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(sdk): scope the slow-types note to what deno actually rejects
Deno's fast check only rejects a return type it cannot trivially infer;
setClient, appendToResultStream and streamResult are all exported without
one and publish fine. The previous wording read as if the current list were
already non-compliant.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>