* test: assert the unpacked repo symlink without following it
`unpack_keeps_a_link_that_stays_in_the_repo` read through the link it had just
unpacked. Windows stores a symlink's target verbatim and its object manager
rejects the `/` in a POSIX one, so `read_to_string` came back with
`ERROR_INVALID_NAME` and the release's `cargo_test_windows` job was red.
Pin what the function is responsible for on every platform — the link is kept
and materialized — and read through it only where a POSIX relative target
resolves.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx
* test: key the cli sync-map fixtures with the platform separator
A sync map is keyed with the platform separator on both sides — `FSFSElement`
walks the tree with `path.join`, and the remote `ZipFSElement` starts at
`"." + SEP` and joins from there — while an `!inline` reference is always
forward-slash. `lock_dedup.ts` follows that convention; the fixtures did not,
so on Windows they built a map shape the CLI never produces and 12 of them
failed. `getTypeStrFromPath` is the same story: it matches
`"dependencies" + SEP`, and the test handed it a forward-slashed path.
Build the fixture keys through the separator, leaving the `!inline` references
and the `present` map forward-slash, as `sync.ts` hands them over.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx
* ci: skip the discord comment relay when the thread lookup returns none
A rate-limited or unauthorized Discord response carries no thread list, and
under `bash -e` that aborted the step — jq cannot iterate null, nor parse the
HTML error page Cloudflare answers a 429 with — before it reached the "thread
not found, skipping" branch right below. Three comment relays failed that way
on the 1.794.0 head.
Keep the step green for both, but tell them apart: a response with no thread
list is a delivery that was dropped for a reason worth seeing, so it warns with
the body it got, while a PR that genuinely has no thread stays quiet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: support application default credentials for gcp pub/sub triggers
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: address review findings on gcp application default credentials
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: address review nits on gcp application default credentials
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: key the gcp credential-mode permission off the loaded mode
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: gate enabling an ADC gcp trigger on workspace admin
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: lock the gcp trigger row while authorizing a mode change
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: skip admin-only gcp listing when the caller cannot use those credentials
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore: update ee-repo-ref to 54bf630681000c8ed87a7067e357118e015123b1
This commit updates the EE repository reference after PR #738 was merged in windmill-ee-private.
Previous ee-repo-ref: 91d0e228a0ad226625278b400c64f96a61404a10
New ee-repo-ref: 54bf630681000c8ed87a7067e357118e015123b1
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* feat(cli): deduplicate identical script lockfiles into one per language
* test: pin shared lockfile path classification
* fix(cli): never delete a lockfile the dedup plan also writes
* fix(cli): plan lockfile dedup from the whole tree, not the sync scope
* fix(cli): keep dedup out of dry runs and stop hiding scripts from its scan
* test: pin which files the shared-lock scan counts as readers
* fix(cli): validate shared-lock refs and let the majority keep its file
* fix(cli): snapshot shared-lock ownership before regeneration moves it
* fix(cli): address dedup review nits (dry-run push, json shape, scan scope)
* refactor(cli): put shared lockfiles in a top-level locks/ directory
* fix(cli): claim only the shared lock names windmill writes, and only when on
* fix(cli): read the lock field itself, and count only scripts sync reads
* fix(cli): parse metadata by its real format and lint from the sync root
* fix(cli): never re-hash a script whose generation failed
* fix(cli): share sync's walk exclusions and fail closed on unreadable dirs
* fix(cli): keep a lockfile the metadata on disk still references
* fix(cli): decide a lock is unread from the metadata field sync reads
* refactor(cli): name shared lockfiles after the dependency file they resolve
* fix(cli): carry shared lockfiles a narrowed sync cannot speak for
* fix(cli): move a shared lockfile when its dependency file moved, not on a head count
* fix(cli): let the many correct a shared lockfile a lone variant planted
* fix(cli): read why a lock differs from the stamp the worker writes into it
* fix(cli): let an agreeing majority speak whatever the stamps say
* docs(cli): count the disjuncts the comment introduces
* fix(cli): keep a private lock for any script the worker locks differently
* fix(cli): match annotations by the worker's own names, not by shape
* fix(cli): recognize the py: interpreter pin the macro does not cover
* fix(cli): let the map speak for dependency-file deletions
* perf(cli): group lock entries without rebuilding the group per insert
* fix(cli): defer shared-lock deletions until the metadata has settled
* fix(cli): decide shared-lock readers by the lock field, failing closed
* fix(cli): read folded lock refs and keep locks read by unparseable metadata
* refactor(cli): one shared-lock reader scan, shared by the pull and push paths
* fix(cli): keep nested dependency set names out of shared lockfiles
* fix(cli): drop a shared-lock scan gate that no real repo took
* feat: teach the AI the raw-app job bindings and the draft/deployed split
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: scope the raw-app deploy advice to the referenced item, and stop kind-conversion from stranding fields
The draft/deployed guidance added in the previous commit was read as "deploy the
app too": the agent asked for both the flow and the app and routed a one-item
dependency through the review-and-deploy page. Only the referenced flow or
script has to exist deployed — the preview runs the app's draft — so the prompts,
the `write_app_runnable` warning and the testing rule now say to offer that one
deploy and leave the app a draft.
`buildPersistedRunnable` spread the existing runnable when rewriting it, so
converting a path runnable to inline left `runType`/`path` behind (and the
reverse left `inlineScript`). `isRunnableByName` matches the inline branch
first, so an app "wired to a flow" silently ran stale inline code.
`test_run_app_runnable` now fills ctx-bound inputs with `$ctx:<prop>` the way
RawAppBackgroundRunner does, so a ctx argument no longer arrives missing.
The SDK-reference rationale claimed WM_TOKEN may be unset, that a missing base
URL falls back to localhost, and that a job token is scoped enough to 403 a
hand-rolled REST call. None of the three is true, and it shipped to every
write-script prompt; the text now only says the client configures itself.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review round on the raw-app AI instructions
The eval case could pass on the exact answer it exists to reject. Every
`requiredMentionsAnyOf` alternative but one was flow-agnostic, so "the app must
be deployed" satisfied "must be deployed". All alternatives now name the flow,
and a unit test pins that the app-only phrasing fails.
`instanceLine` asserted "self-hosted Community Edition" outside the browser,
where `isCloudHosted()` reads false and the license store is unset — so every
global eval was told that regardless of what it pointed at. It is now emitted
only under BROWSER.
`assistantExpect.forbiddenMentions` defaulted a missing `assistantText` to "",
which passes every entry forever on a mode whose runner does not report it.
It now fails with that as the reason.
`buildPersistedRunnable` carried `schema` across a retarget, so a path runnable
pointed at a new flow kept the previous item's schema and `genWmillTs` typed
`backend.<key>(args)` from the wrong inputs. It survives only while kind and
path both match.
The SDK header claimed "a function that is not listed below does not exist".
`windmill-client` also exports the generated services, and the Python client
exposes `Windmill.get`/`.post`, so an endpoint without a helper had no legal
move. Each language now names its own escape hatch.
`getAppInstructions` said the attached reference carries the TypeScript SDK even
when `language: "python3"` had swapped in the Python one — on the very sentence
telling the model to make that call.
The kind-conversion comment claimed a hybrid runnable "silently runs stale
inline code". It does not: `isRunnableByName`, `isRunnableByPath`,
`convertPersistedToBackendRunnable` and `rawAppPolicy.processRunnable` all
dispatch on `type` alone. The leftovers contradict the runnable's kind rather
than override it, which is what the comment now says.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address round-2 review nits on the raw-app AI instructions
`flow is deployed` was satisfied both by "once the flow is deployed, the button
works" and by a hallucinated "done — the flow is deployed", which eval mode makes
impossible and the drafts-only judge cannot see. Every alternative now states an
outstanding obligation, and two more real phrasings ("will need to be deployed")
are accepted so a correct answer is not failed on wording.
Condenses the three comment blocks that ran past the four-line limit in
AGENTS.md, and drops two claims inside them that no longer hold: the
`testRunAppRunnable` doc said it runs a runnable the way the app's own frontend
does (it is the editor preview, which a deployed app's stored policy does not
match), and `undeployedRunnableTargets` described its argument as the write
tool's raw input when the call site passes the persisted runnable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: report the real cause when a test run fails, and label the app-runnable card
Driving `test_run_app_runnable` in a live session surfaced two defects the
API-level check could not see.
`executeTestRun` built its failure message from `error.message`, which the
generated client leaves as the bare status text while the server's message sits
in `body`. A path runnable aimed at an undeployed flow reported "Not Found"
instead of "Not found: flow not found at name u/admin/current_time" — dropping
the one diagnostic the run exists to produce. `formatToolError`, in the same
file and written for exactly this, now does it. This also applies to
test_run_script and test_run_flow, which had the same loss.
The completion card read "Flow test completed successfully" for an app runnable,
because `contextName` doubles as the jobs-tray kind and a path runnable pointing
at a flow really does queue a flow job. A `completionName` override now names
what ran without changing the kind.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: pin the deploy expectation against wrong answers, not just correct ones
`deploying the flow` was satisfied by "done deploying the flow" — a deploy the
agent only claims to have made, which eval mode makes impossible and the
drafts-only judge cannot see. Replaced with the prospective forms, and dropped
the same reading from the workflow variant.
Three review rounds each found this same class of hole in the phrasing list, so
the list is now exercised against the wrong answers themselves rather than
eyeballed: naming the app as what needs deploying, claiming the deploy is
already done, claiming to have deployed the flow, and saying nothing about
deploying all have to fail, while four real correct phrasings have to pass. The
test reads the case out of global.yaml, so a future edit to the alternatives is
checked by it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: drop the tense-neutral deploy alternatives and cover completed claims
A gerund after a preposition carries no tense, so `before`/`after`/`by deploying
the flow` all match a deploy the agent only claims to have made ("after
deploying the flow, I clicked the button and it returns the greeting") just as
the bare gerund did. All three are gone rather than swapped for whichever reads
least badly, and the two completed-deploy phrasings are now negative fixtures.
The remaining alternatives are imperative or obligational, which a claim of
having already deployed cannot satisfy.
Condenses the two comments this list carries: the YAML block to four lines, and
the test's rationale to the durable constraint about substring matching.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: encrypt sensitive inputs when test-running an app runnable
`test_run_app_runnable` sent `force_viewer_static_fields` but not
`force_viewer_sensitive_inputs`, which every other preview path derives from
the runnable's `sensitive` user fields. That list is the only thing driving the
encryption loop in apps.rs, so testing a runnable with a sensitive input wrote
the real value into the job's args in plaintext, readable by anyone with run
access to the workspace.
Verified against a running EE instance. With the list, `api_key` is stored as
`$encrypted:mvqtSRI9…` and the sentinel appears nowhere in the job record;
without it, the sentinel is readable in run details. A non-sensitive field is
left plaintext either way.
The tool claims parity with the editor preview, so it uses that same filter
(`type == 'user' && sensitive`) and omits the field entirely when empty.
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: Ruben Fiszel <ruben@windmill.dev>
* fix: reject invalid AI agent tool names when the chat writes a flow
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review nits on agent tool name validation
Share one AI-agent walk between the providerless-agent and invalid-tool-name
collectors, drop the unused validateToolName, and list every reserved id in the
tool naming rules.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: describe an agent tool's summary as the name the agent calls it by
The OpenFlow schema described `AgentTool.summary` as a short description of
the tool, which is the same schema the flow write tools hand the model, so it
pulled against the naming rules. Narrow those rules to flowmodule tools, since
websearch and mcp tool names are never regex-checked, and let `kind` take
either vocabulary its callers resolve.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: name-check only the agent tools whose summary the agent calls
An mcp tool exposes the MCP server's own tool names and a websearch tool's
summary is a plain label, so neither reaches the worker's name check. Both
default to an empty summary in the editor, which the chat then refused to
write back.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): keep script retention, debounce and cache settings on push
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cli): surface the create response when the fixture fails
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cli): drop debounce settings the CI build refuses to accept
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* repair the script push up-to-date comparison (#10743)
* test: settle the backlog before the capped audit-export drain (#10737)
* test: settle the backlog before the capped audit-export drain
* chore: update ee-repo-ref to bd4de74eb37b32a2b6c7c69f6dedac031ef8436b
This commit updates the EE repository reference after PR #730 was merged in windmill-ee-private.
Previous ee-repo-ref: b5a5f9114df26088cfe976d91f10e55ba8bfcaa6
New ee-repo-ref: bd4de74eb37b32a2b6c7c69f6dedac031ef8436b
Automated by sync-ee-ref workflow.
---------
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix(cli): repair the script push up-to-date comparison
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cli): drain dependency jobs and pin a non-1 priority skip
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cli): describe the priority fixture without the old comparison
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: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor(cli): read cache_ignore_s3_path off the typed response
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): stop redeploying bunnative scripts on every push
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: default an omitted app policy execution_mode to publisher
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: drop stale comments claiming execution_mode is required
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: derive a raw app's policy on deploy instead of trusting the caller's
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: pin the ee ref to the companion branch
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: vendor the raw-app policy derivation into the bundle job
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: note the vendored raw-app policy bundle
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: derive the policy on a value-only raw-source update too
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: reject raw-app runnables whose shape yields an unusable grant
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: cache the new policy query and tighten raw-app runnable validation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: let the policy bundle drift guard survive a CRLF checkout
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 23431f5cf1d627051ded89111bbf2e301e9db456
This commit updates the EE repository reference after PR #729 was merged in windmill-ee-private.
Previous ee-repo-ref: 0bdf8818fa115ad6b0d14f3117a18e8a580cce4d
New ee-repo-ref: 23431f5cf1d627051ded89111bbf2e301e9db456
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>
* feat: add trigger_history table with source tracking
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: gate trigger history reads on scopes and harden its writers
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: filter trigger history scopes in SQL and match the cleared-handler diff
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: record a trigger restore from the trashbin in its history
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: record bulk http trigger creates and document the recording boundary
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: lock the trigger row when capturing its history preimage
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: only record an auto-disable that actually flipped the schedule
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: state the auto-disable invariant once instead of at four call sites
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: render trigger history changes as a structured field diff
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: make a server-initiated disable atomic with its history row
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: note that the auto-disable savepoint takes no pool connection
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: note the flow fallback is the last chance to disable
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: never leave a trigger enabled because its history row failed
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: retry the disable history row instead of dropping it on first failure
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: use the design-system Button for the change-value expander
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: hold the trigger row lock across its disable history row
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the history-loss alert out of the listener cancellation race
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: read the history workspace through the trigger-workspace seam
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <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>
* 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(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>
* 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>
* feat(sdk): add cancelJob to the TypeScript client
The Python client has had cancel_job since forever; the TypeScript one had no
way to cancel a job at all. Wire the same jobs_u/queue/cancel endpoint, with a
default reason when none is given, and export it from both the named and
default exports of the npm package as well as the JSR one.
* chore: regenerate system prompts for cancelJob
check-system-prompts triggers on typescript-client/**, so the agent-facing SDK
reference has to carry the new function.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Tushar <tusharanshu18@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): attach the right job path to preview runs
* fix(cli): keep a deliberately-absent file in the directory it was named in
* fix(cli): resolve links for a path naming a file that is not there
* fix(cli): sync package-lock.json with package.json
`npm ci` fails in cli/ because the lockfile predates two manifest changes:
windmill-parser-wasm-yaml was bumped to 1.770.0 and windmill-yaml-validator
1.1.1 was added, neither of which reached the lockfile.
Regenerated with `npm install --package-lock-only`; the only entries touched
are those two packages and windmill-yaml-validator's transitive deps.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): drop the unread npm lockfile, bun.lock is the CLI's
Every install path in cli/ runs `bun install`: cli-tests.yml, git-sync-test.yml,
backend-test.yml, build.sh, and install_dev.sh (its --node branch installs the
generated npm/ bundle, which carries its own manifest). build-npm.ts synthesises
the published package.json from scratch, and change-versions.sh regenerates the
frontend and yaml-validator lockfiles but not this one.
So package-lock.json was read by nothing and verified by nothing, and drifted out
of sync with package.json unnoticed until `npm ci` refused to install. Deleting it
removes the second source of truth rather than hand-repairing it again on the next
bun-driven dependency change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: allow custom dev workspace environment labels
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: reject dev labels that shadow a tracked branch's namespace
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: guard dev labels against a repo's assumed default branch
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: state the badge-cap rationale once and drop unenforceable openapi constraints
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: offer a fixed list of environment labels instead of free text
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: match the accepted label set to the openapi enum exactly
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: stop describing the label set as dev/staging only
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: deploy a raw app from its sources, bundling them on a worker
* refactor: bundle raw app sources with the wmill CLI instead of a second bundler
* fix: address review findings on the raw app source deploy
* fix: bound bundle decompression, drop the npm dependency on slim workers
* fix: stop minting jobs:run for the source deploy, share the decode budget
* feat: let an MCP token grant the scopes its selected tools require
* fix: carry a caller-held extra scope through the MCP proxy
* fix: confine the run scope to the proxied request instead of the token
* fix: mint the run scope only for a token that names the tool
* fix: require write access before compiling, and state the grant where it is granted
* fix: let the database decide write access instead of restating its policies
* fix: answer a write denial with 403, not 401
* feat(fork): merge a fork deletion on evidence, not on the counters
`workspace_diff.ahead`/`.behind` record that a write happened on a side,
not what it was or who made it. That leaves one row shape undecidable: an
item the parent has and the fork does not can mean the parent added it,
the fork deleted it, or a git-sync pull reverted a deploy that had just
brought it in. #10467 kept every such row out of the merge direction,
which killed the phantom but also dropped the only way to propagate a
fork-side deletion and left a rename's old path behind in the parent.
Record the evidence instead:
- `workspace_diff` gains, per side, the last event's kind (`write` /
`delete` / `rename_from`) and origin (`authored` / `sync`). Rows
written before the migration have neither and keep #10467's behavior.
- The kind is probed from whether the path still holds an item once the
write has committed; an item kind the probe doesn't map records no
evidence rather than a deletion. Create and update are not split —
nothing at that point tells them apart for every kind, and the
comparison already recomputes existence per side.
- The origin comes from an `X-Windmill-Deploy-Origin` header the API
scopes into a task-local for the request. It is the load-bearing half:
recording `delete` alone would read a git-sync revert as a fork
deletion and reproduce the original bug. Two clients set it — `wmill
sync push` (which the git-sync auto-pull runs inside a job) and the
compare page's parent→fork "Update fork". Merging the other way stays
authored so a deletion keeps propagating up a fork chain.
- The merge direction admits a parent-only row only when the fork's last
event was an authored delete or rename-away. Such a row stays opt-in,
never bulk-selected, and reads "Removes in <parent>"; the update
direction keeps offering it back as "New".
A fork deletion and a rename now merge into the parent, a rename leaves
no duplicate behind, and a fork the parent also edited surfaces in both
directions instead of the parent silently winning.
Fixes WIN-2289
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(fork): address review — detached tallies, enum wire values, doc duplication
Codex P1: a dependency job tallies its deploy whenever it happens to finish,
and the event kind is probed from the state at that moment. If anything
removed the path in between (a git-sync revert), the stale tally read that
deletion as its own and filed it as authored — handing the merge exactly the
removal this is meant to withhold. `tally_deployed_object_changes` now takes
`Option<DeployOrigin>`; `None` bumps the counter and leaves the evidence
columns as the last vouching tally left them, and the worker path passes it.
Covered by extending the removal-origin test: a detached tally after the sync
archive must not disturb `(delete, sync)`.
Also from review:
- `fork_removed_it` compares through `DeployOrigin::as_str()` /
`DeployEventKind::as_str()` rather than repeating their wire values, so a
renamed variant can't silently make the predicate always false.
- `deploy_origin`'s module doc no longer claims `sync` is inert: it cannot
make the merge propose a removal, but it does drop a row out of both sides
of the `all_ahead_items_visible` comparison.
- `WorkspaceDiffRow` says why only the fork half of the evidence is consumed.
- The delete-vs-revert rationale is stated once (the migration) instead of
restated in eight files.
- `PATH_KEYED_TABLES` is swept by a test: its query is built at runtime, so a
wrong table name is not a compile error and would only surface as a failed
tally for that trigger kind in a fork.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(fork): let only a request task vouch for a deploy event
Round 2 found the first fix incomplete. Detaching only the failed/cancelled
dependency path left the common route untouched: a dependency job that
succeeds calls `handle_deployment_metadata` from the worker, where
`deploy_origin::current()` read as `Authored`. A sync archiving the script
while its lock generation was pending then had its deletion probed on
completion and refiled as authored — the same fabricated removal, on the
path most deploys actually take.
`current()` now returns `Option`, `Some` only inside the request scope the
API always enters. Having no scope means "not the task that served this
write", which is true of every worker-side call and needs no marking at the
call site. The integration test drives the real `handle_deployment_metadata`
off a request task instead of the tally directly, and fails without this.
Two more from the same round:
- The script dependency handler passed no `renamed_from`, unlike the flow
and app handlers next to it. A lock-generating create has no earlier
tally, so that was the only chance for the path a rename vacated to be
recorded at all — renames of Python/TS scripts left the old path in the
parent, which the bash-only manual check missed.
- The tally now drops a `renamed_from` equal to the path itself. Callers
pass the previous path whether or not the deploy moved the item, so an
unfiltered one both counted the path twice and stamped it `rename_from`
when nothing was renamed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(fork): carry a deploy's origin into the dependency job it queues
Round 3 caught the previous fix cutting too deep. Refusing a detached tally
any claim also refused its rename evidence, and a lock-generating deploy has
no other tally — so the `renamed_from` added alongside it was inert, and a
renamed flow, app or Python script still left its old path in the parent
with nothing to merge. Flows and apps always generate, so renames worked
essentially nowhere.
The two capabilities are now separate. `TallyEvidence` says whether the
tallying task served the write (`Served`, may probe what the path holds now)
or is reporting one that committed earlier (`Deferred`, may not), and each
column is written only from a source that answers for it. The origin itself
is a fact of the deploy either way, so the request stamps it into the
dependency job's args and the worker re-enters the scope with it — the last
place that knows it handing it to the only tally that will run.
Also from round 3: `WorkspaceDiffRow`'s event fields skip serializing `None`
rather than emitting `null`, matching what the schema declares (OpenAPI
3.0.3 ignores a `description` sibling of `$ref`, so those moved onto the
shared schemas).
Verified against a live worker: renaming a flow in a fork records
`(rename_from, authored)` on the vacated path and the merge offers its
removal, while the deployed path claims nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(fork): mark the CLI's parent-to-fork merge as sync
`wmill workspace merge --direction to-fork` is the CLI's "Update fork" and
deletes items in the fork, but without the marker the compare page sets. Its
deletions were recorded as authored fork decisions, so once the parent
recreated such a path the merge would offer deleting it there.
Also from review: an unrecognized deploy-origin arg now reads as no evidence
rather than as authored — strict where a request header is lenient, since an
unmarked request really is authored but an unreadable stored value is skew.
Reading the arg moved next to `stamp_origin_arg`, the half that writes it, so
the round trip a lock-generating deploy depends on is covered by one test.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: drop the imports the shared arg reader made unused
CI compiles with `-D warnings`, so this was four red Backend jobs rather
than a lint.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(fork): stop a stale deferred rename from restating a removed path
Nothing orders these events. A tally that served the write made its claim
inside its own commit, but a deferred one reports a write that landed at an
unknown remove. So a lock-generating rename whose dependency job finished
after a sync had removed the vacated path could overwrite `(delete, sync)`
with `(rename_from, authored)` — the path is gone either way, so the merge
would then offer removing it from the parent on the strength of the older
event.
A deferred claim now only writes where the side has none, which is the case
it exists for: a vacated path that nothing else has spoken for. The
regression asserts the ordering directly, and fails without the guard.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(fork): record a rename's vacated path from the request that made it
The deferred mechanism could not be made correct, as round 7 showed: its
guard protected an existing row, but that row is deleted as soon as the two
workspaces agree on the path — so a rename job finishing after the
reconciliation inserted fresh, and the stale claim reappeared against
whatever the parent later recreated there. Ordering cannot be recovered
outside the row, because the row is disposable.
So the vacated path is now recorded by the request, which is inside its own
commit and whose row shares the counter's lifetime. A deploy that hands its
metadata to a dependency job — every flow and app, and any script needing a
lock — calls `tally_rename_vacated_path` once its transaction has committed;
scripts reach it through the post-commit hook they already had, which grew a
second variant rather than new plumbing.
That lets the whole deferred apparatus go: `TallyEvidence`, the origin job
arg and its round trip. `deploy_origin::current` is `Some` only inside a
request scope again, and `handle_deployment_metadata` hands `renamed_from`
to the tally only when it can answer for it — git-sync still gets it either
way, so the rename keeps naming itself in the commit message.
The vacated path's kind now reads `delete` rather than `rename_from` for
these deploys, since it is probed rather than declared. The merge treats the
two alike; only the row's tooltip is less specific.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(fork): cover raw-app renames, and stop firing CI before the lock exists
Two things the vacated-path call broke or missed:
- `create_script` reads its third return value as "no lock generation
needed" to decide whether the script is runnable now, and the new
`VacatedPath` variant made that true for renames that do generate. Those
fired dependent CI tests from the API against a version with no lockfile,
and again from the dependency job. The variant now decides it explicitly.
- Raw apps rename through `update_app_raw`, a separate route into
`update_app_internal`, which the new call had not been attached to. Both
routes now go through one helper.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(fork): assert the kind only an inline rename can record
`rename_from` is what a deploy says when it knows it moved the item, which
only the path that reports both halves from its own request can. Nothing
pinned it, and that is the side the vacated-path change touched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to a45bec03922d305aad5893ed354dc029c7f97bb4
This commit updates the EE repository reference after PR #709 was merged in windmill-ee-private.
Previous ee-repo-ref: 62f494b2a51de0dfc0cfa0c3530ff19a1d32667c
New ee-repo-ref: a45bec03922d305aad5893ed354dc029c7f97bb4
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>
* feat: add session recording to wmill app dev
* fix: harden dev recorder shell, bundle staleness guard and save route
* fix: keep dev-server recordings out of the raw app sync diff
* style: drop em dashes from new cli comments
* fix: tighten dev recorder save route origin, naming and io
* test: pin that only the root recordings folder is skipped
* fix: survive an oversized recording upload and match paths on windows
* fix: keep the app at the root and settle runnables stranded by a reload
* fix: make the recorder bundle hash stable on a crlf checkout
* docs: state the preflight-free content type the origin check guards
* test: build the sync-skip fixture with the platform separator
* docs: align the origin-guard test comment with the code
* chore: mark generated .gen.ts files as generated