* feat: windmill-chat sdk for chat-mode flows in external frontends and raw apps
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aQiZNAU8g17kWkyTryS5J
* fix: keep streamed answers until persisted, finish turns after history fallback
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aQiZNAU8g17kWkyTryS5J
* feat: ai sdk transport and assistant-ui runtime for windmill-chat
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix: finish a turn from the flow result until its answer row lands, hash chat ids without crypto.subtle
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix: judge a turn answered by a persisted assistant row, wherever it was fetched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix: attribute a turn's answer to its own jobs, keep a local turn when switching conversations
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix: mirror local history on every change, attribute failure-handler answers to the turn
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix: new chat per token string in the React hook, idle after destroy, no reorder on view
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix: recreate the hook's chat on any credential change, namespace local history per user
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix: send the latest inputs from the React hook
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* feat(cli): list, get and restore trashed items from the CLI
* docs(cli): tell agents a sync push deletion is restorable with wmill trash
* refactor(cli): share the ApiError formatting and type trash flags as integers
* fix(cli): keep variables and resources a sync push repo never tracked
`wmill sync push` archives a script it no longer finds locally, but hard-deletes
a variable or a resource: the credentials go for good. A remote-only one is
equally a deletion being deployed and one the repository never had, provisioned
on the instance or written by a script at runtime, and reading the second as a
deletion is unrecoverable.
Committed history tells them apart. A push whose changeset deletes a variable or
resource now asks what this branch has ever tracked at `*.variable.*` /
`*.resource.*`; anything it has never recorded is kept on the remote (prompted
for on a TTY), and a real deletion, recorded before the commit that removed it,
still applies. Where the history cannot be read (shallow clone, sparse checkout,
no repository) there is no evidence either way, so the deletion stands as before
with a warning naming the remedy — the git-sync "Pull from repo" job runs in a
depth-1 clone and must keep deploying the deletions it always has.
`--delete-untracked-secrets` / `deleteUntrackedSecrets` opts a mirror-semantics
pipeline back into deleting them unattended.
Fixes GIT-980
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): classify secret-bearing deletions the way the push itself does
Three ways the suffix match missed:
- A fileset child can be any file, `inner.resource.yaml` included, and its
deletion re-pushes the parent rather than deleting anything. Classifying with
the push's own `getTypeStrFromPath`, behind the same fileset exclusion the
apply loop uses, keeps the two in step.
- Deleting `f/x.resource.file.ini` deletes the resource `f/x` outright, so
without that file in the pathspecs every file resource walked past the check.
Its two files now count as the one resource they delete.
- A `specificItems` item is committed as `y.<workspace>.variable.yaml` while
`elementsToMap` collapses it to the base path the changeset carries, so a
deletion the user did commit read as never tracked. The history is searched
under both names.
`gitRecordedPaths` also reads its history with `core.quotePath=false`: a path
with a non-ASCII byte came back C-quoted and matched nothing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): judge held-back deletions per object, not per file
`DELETE /variables/delete` takes the resource at the same path down with it, and
`DELETE /resources/delete` does the same to the variables its value references,
so a tracked deletion could destroy an untracked object the push had just
reported it was keeping. A file resource had the same shape from the other end:
two files for one resource, either survivor deleting it.
The unit is the server-side object. One file left unaccounted for by history now
holds the whole object back, so nothing in a group reported as kept is deleted.
The residual is a resource whose value references a variable at another path,
which stays possible and is called out in the PR.
Also corrects what the messages claim. Deleting a variable or resource is not
irrecoverable: both move to the workspace trash, which keeps them for three days
(migrations/20260326000000_trashbin.up.sql, CE since v1.665.0). The asymmetry
with a script is real but narrower, and the prompt defaults to No, so it should
say what it actually costs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): warn when sync push deletes variables the repo never tracked
`sync push` deletes a remote variable or resource that has no local file. An
object the repository has never tracked was provisioned outside it, by hand or
by a script at runtime, rather than deleted from it, and the change list said
nothing to tell the two apart.
The push still deploys every deletion — that is what the repo-is-the-mirror
contract means, and a shallow clone (the git-sync "Pull from repo" job, a
default actions/checkout) could not tell them apart anyway. What changes is that
the preview names the ones this branch's history has no record of, before the
prompt that confirms them, and points at the excludes that stop them recurring.
Deleting is also not final, which the CLI was alone in not saying: both handlers
move the item to the workspace trash first, restorable for three days
(migrations/20260326000000_trashbin.up.sql, CE since v1.665.0). A push that
deleted any now says so.
This replaces the earlier hold-back design. Keeping objects back changed what a
push deploys, needed a flag and a wmill.yaml key to opt out of, and could claim
to keep an object that a linked deletion then cascaded onto. Reporting cannot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): name the paths the untracked-deletion warning is about
A push can delete a tracked and a never-tracked resource together, where "1
resource" identified neither. The warning lists the paths instead of counting
them, so the reader knows which one to exclude.
Outside a git checkout it no longer opens "This branch's history", which
contradicted the reason it went on to give, and it drops the pronouns that
disagreed with a plural count. The history walk is skipped under --json-output,
where both notices are silenced and its result had no reader.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): vouch for an object with any file in history, not just deleted ones
The tracked set was built from the deletions being judged, so a companion file
the push was not deleting could not vouch for its object: a file resource whose
`.resource.yaml` stays while its content file goes was reported as never
tracked, though the repository plainly owned it. It is built from the whole
history now, which also turns the workspace-specific lookup around — history is
normalized to base paths, the form the changeset already carries, instead of
each candidate being searched for under two names.
The warning also prints one line per server-side object rather than per file, so
a file resource is the one deletion it is rather than two.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): name both kinds at a shared path, gate the history path conversion
Three from review:
A variable and a resource at one path are judged together, since deleting either
takes both, but they are two objects to name — keying the printed lines by path
alone dropped one of them.
`fromWorkspaceSpecificPath` strips a `.<workspace>` segment wherever it finds
one, so a history entry that merely looks workspace-suffixed was re-keyed onto a
different object, whose history then vouched for it. Only a path `specificItems`
claims is converted now. Not reachable from a server object (the backend rejects
`.` in paths), but history holds whatever was committed.
`secretBearingKey` lost its last caller when the tracked set moved to object
paths; removed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): identify a secret-bearing object by kind as well as path
A variable and a resource can share a path and are still two backend objects:
`DELETE /variables/delete` drops the same-path resource unconditionally, while
`DELETE /resources/delete` drops only the variables its value references. Keying
tracked history by path alone let a committed variable vouch for a resource the
repository never had, which then went unmentioned. The cascade is a reason to
report both, not to treat them as one. A file resource's two files keep one id.
`git log HEAD` also fails on a repository with no commits, which was reported as
"its history could not be read. Check that git runs correctly in this directory"
— true of neither.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(cli): tighten the comments on the untracked-deletion warning
Halves the prose without dropping a constraint: the trashbin retention is
stated where the message says it rather than twice more in doc comments, and the
two stacked comments at the print site had come to contradict each other, one
still describing a same-path variable and resource as judged together.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): say where a deleted variable or resource went
`sync push` deletes a remote variable or resource that has no local file, and
said nothing more. Both handlers move the item to the workspace trash first,
restorable for three days (migrations/20260326000000_trashbin.up.sql, CE since
v1.665.0), and the CLI was the one surface never to mention it — the report
behind this concluded the deletion was final and there was nothing to restore.
A push that deleted any now ends with where they went and how long they have.
Drops the untracked-deletion warning this branch carried: distinguishing a
deletion the repo deployed from an object it never owned needs the branch's git
history, and roughly 130 lines to read it and be right about the answer, for a
claim the trash already softens. Two fixes it turned up in the data-table
migration guard, which reads history the same way, are kept: a path with a
non-ASCII byte came back C-quoted and matched nothing, and a repository with no
commits was reported as one where git does not run.
Fixes GIT-980
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): name the trashbin correctly and say who can restore
The tab is labelled Trashbin, not Trash, and `restore_trash_item` requires
admin, so a non-admin reading the old line would go looking for a control they
do not have.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(cli): move the migration-guard git fixes to their own PR
They fix `gitRecordedDatatableMigrationPaths`, which this PR no longer touches.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): don't count a .lock deletion the push skips
The apply loop `continue`s past a non-raw-app, non-dbt `.lock` deletion before
reaching the delete switch, so nothing happens on the server. The classifier did
not mirror that, and `f/x.resource.file.lock` reaches it as a resource through
`isFileResource` — a resource type whose format_extension is literally `lock`
would have the notice announce a deletion the push never performed.
A raw-app or dbt `.lock`, the two that loop does not skip, classifies as its
bundle's own kind well before the file-resource check, so a suffix test is
enough.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(cli): state what the classification tests protect
The header described the change rather than the invariant.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat: add per-route CORS origin allowlist for HTTP triggers
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: fail closed on cold router cache and invalid origin input
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: resolve CORS route from the decoded path like the request handler
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: add instance-wide default allowed origins for HTTP routes
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: let non-superadmins read the default allowed origins setting
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: badge the advanced section when a route's origins are restricted
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: state inherited origins on the control and use one hint row
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: trim the origins tooltip and relabel the toggle when a default exists
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: keep the origins format hint visible until an entry is wrong
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: state the at-least-one requirement in the origins hint
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: import the origins validator in the trigger-http tests
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: make an empty allowlist deny rather than fall back to the default
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: address review nits on origin validation and the CORS editor
* fix: derive the origins error from the stored list and tighten host validation
* fix: parse real IPv6 hosts and refuse a newly emptied allowlist
* refactor: make origin validation advisory except for null and non-ascii
* feat: let an empty allowlist be saved as deny every origin
* docs: document the empty allowlist as deny every origin
* fix: bound allowlists, reject commas, and decide cors after the handler
* chore: revert unrelated rustfmt churn in windmill-common tests
* chore: revert unrelated rustfmt churn in windmill-common
* chore: drop the route types the cors restructure replaced
* fix: take the stricter cors decision from before and after the handler
* fix: strip runnable cors headers when the routers are unavailable
* docs: document the allowlist bounds in the openapi schema
* fix: let an unavailable cors read defer to one that resolved
* refactor: carry the resolved cors policy from the handler to the middleware
* docs: describe why an unavailable read fails closed on the paths that reach it
* fix: validate the default origins on the declarative settings path
* test: keep the webhook doc comment with the test it describes
* fix: warn on impossible schemes and ports, and validate the instance setting
* feat: treat an empty allowlist as unset at both levels
* perf: decode the cors path only when the fallback needs it
* docs: document the empty allowlist as unset in the api schema
* docs: describe an empty allowlist as unset in the frontend comments
* docs: say what a null allowlist resolves to, not what it meant before the default existed
* docs: state what the validator refuses and why methods stay broad
* feat: exempt static asset routes from the origin allowlist
* fix: hide the origin control for every static target, not just websites
* fix: exempt only static websites, not single-file static assets
* fix: warn on an unclosed ipv6 host in the origins advisory
* fix: require assets present, not just the static website flag
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat: let a native trigger be disabled without deleting it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: show and control the native trigger pause outside the flow editor
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: create a native trigger already paused instead of pausing it after
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: correct the native trigger enabled comments for create-time init
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: recognize `// volume:` mounts in PHP scripts
Volume annotations were parsed for every language but PHP, so a PHP script
could not mount a workspace volume. Two things stood in the way: PHP had no
entry in the comment-prefix maps, and a PHP script opens with `<?php`, which
ends the leading comment block the parsers scan before any annotation is read.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3FR7iS9nRhpFt615cnuQ7
* fix: tolerate a PHP opener that carries code, drop the inert CLI hunk
The open-tag skip matched `<?php` exactly, so `<?php declare(strict_types=1);`
still ended the leading comment block and every annotation below it was silently
ignored. Match the tag as a case-insensitive prefix and skip the whole line.
The CLI local-graph hunk could never fire: PHP has no wasm asset parser, so
`fallbackParse` handles it, and its own header scan stops at `<?php` — the script
is dropped as a non-pipeline-member before any volume asset is read. Making only
the CLI PHP-aware would also put the local graph out of parity with the deployed
one, whose `parse_pipeline_annotations` stops there too.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3FR7iS9nRhpFt615cnuQ7
* docs: correct the CLI mirror comment, state the own-line annotation rule
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3FR7iS9nRhpFt615cnuQ7
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): keep permissioned_as on single-item push, as sync push does
* fix(cli): resolve syncBehavior from the target workspace, not the branch alone
* refactor(cli): share the workspace-name resolution between sync and single-item push
* test(cli): import the moved workspace-name helper from its new home
* docs: teach agents to pass a resource as $res:<path> in run arguments
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XjRARL7JA7xm772iJP4mJk
* docs: extend run-argument rule to in-editor chats, fix run-as wording
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XjRARL7JA7xm772iJP4mJk
* docs: tighten resource run-argument rule after review
- Drop the false rationale that "$var:" only works inside a resource value
from the write_variable description and its runtime rejection message; keep
the rule (a variable cannot reference itself).
- MCP resource-argument description: the title fallback renders "No title",
so say the title is only a label rather than that it can be empty. Guard the
real-newline fix with asserts in the existing enrichment test.
- Eval: assert the full "$res:f/evals/global/github_main" value as one prefix
so a wrong path with a right prefix fails.
- resources.md: narrow "a trigger's payload" to its configured static args.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XjRARL7JA7xm772iJP4mJk
* docs: scope the run-argument rule to global chat, add an exact eval matcher
The ai_evals A/B on the two in-editor modes showed no effect: script mode
sonnet 5/5 both with and without the description, flow mode sonnet 5/5 and
haiku 5/5 on the baseline alone. A flow's input schema already carries
`format: resource-<type>`, so those modes have a signal global mode does not
give. Revert both files to keep the tool schemas free of a description that
buys nothing per iteration; global mode keeps it, where haiku goes 0/5 -> 5/5.
Add `stringEqualsAnyOf` to toolCallArgs and use it for the resource reference:
nothing in the eval resolves the value, so a prefix match accepted a near-miss
path like `$res:f/evals/global/github_main_backup`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XjRARL7JA7xm772iJP4mJk
* docs: address cubic review — CLI wording, mock resource getter
- `-d --data` help on all four run/preview commands: give $res: and $var:
their own clauses instead of a parenthetical that read as if a resource
were a kind of variable.
- Mock backend: `getBenchmarkResource` now resolves AI-provider seeds as well
as plain ones, so it agrees with `existsResource` and `listResource` — both
report either kind, and a case that listed a resource and then read it by
path got a row it could not fetch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XjRARL7JA7xm772iJP4mJk
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: connect to dev server instead of localhost
* fix: derive WebSocket scheme from location.protocol
Mirror the protocol-aware pattern used by initSqlWebSocket in dev.ts
so the WebSocket connects over wss:// when the dev server is reached
through an HTTPS proxy/tunnel, avoiding mixed-content blocking.
* refactor: drop now-unused port parameter of wmillTsDev
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsfdN82yP88qyQ3h8Lwv2v
---------
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(ai-chat): make reusable skills ai_skill resources you select per workspace
* chore: pin the ee ref to the skill telemetry counters
* fix: address review findings on skill authoring, import and migration
* fix: enforce skill selection in read_skill and stop imports clobbering resources
* feat: carry format_extension from the hub into synced resource types
* fix: let an edit set or clear a resource type's format_extension
* fix: regenerate the sqlx cache and close the review round findings
* fix: close the round-2 findings on folder ACLs, cached sync and truncation
* refactor: make the skills migration non-destructive and use design-system inputs
* fix: close the round-4 findings on folder owners, startup sync and truncation
* fix: clear obsolete extensions, guard folder owners, and report skipped skills
* fix: honor explicit-null extensions and report same-type migration conflicts
* fix: scope skill actions to the committed workspace and paginate the listing
* fix: keep the drawer scoped to the live workspace and surface truncation
* fix: discard a skills refresh for a workspace the chat has left
* chore: update ee-repo-ref to 6efe7a73c745c2e1377a34498523c00d89010a3d
This commit updates the EE repository reference after PR #764 was merged in windmill-ee-private.
Previous ee-repo-ref: 55998c142bc72edd08532748af1974b16035658d
New ee-repo-ref: 6efe7a73c745c2e1377a34498523c00d89010a3d
Automated by sync-ee-ref workflow.
---------
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: keep a local dbt descriptor under sync pull --keep-deleted
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174o6mGTWoanipUgf5zNcVL
* fix: keep an added-shaped dbt descriptor removal under --keep-deleted too
A stateful pull compares `.wmill`, not the working tree, so a descriptor
missing from that map still arrives as `added` while a real file with the
project's warehouse and run arguments sits on disk. Counting only `edited`
left that file deletable, and silently: the flag logged nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174o6mGTWoanipUgf5zNcVL
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: strip the script/ prefix from trigger error handler paths
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: strip the script/ prefix when collecting trigger handler refs
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: relocate prefixed trigger error handlers on project retarget
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: reject a prefixed error_handler_path on triggers instead of resolving it
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: describe error_handler_path as a bare script path in the api schema
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): keep svelte component styles in the raw-app bundle
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: fold svelte style guard into the plugin test file
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: record the editor-parity constraint on the svelte css option
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(cli): pin esbuild's service cwd before any test file chdirs
esbuild's node API captures process.cwd() when its module is first
imported and spawns its service with that cwd on every (re)start.
createBundle stops the service after each bundle, so the cwd is reused
across the whole run.
Several test files chdir into a temp dir and delete it afterwards. The
first one to bundle therefore pinned the service to a directory that
stopped existing, and the next test to reach esbuild died with
The service was stopped: ENOENT: no such file or directory,
posix_spawn '.../@esbuild/linux-x64/bin/esbuild'
The binary is present; ENOENT is posix_spawn rejecting the missing cwd.
Which file tripped it depended on bun's readdir order, so renaming an
unrelated test file was enough to surface it. Importing esbuild from the
preload pins the service to a cwd that outlives the run, independent of
file ordering.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G88YF3sZFnJZUvTLVjqhZc
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* fix: keep raw-app files within their app folder on sync pull
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: validate raw-app file keys as stored, closing nul and duplicate-field bypasses
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: guard raw-app runnable ids too and fail closed on unparseable value
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: strip only a leading slash on raw-app file keys to match backend
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: strip only a leading slash on raw-app file keys to match backend
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* 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>