mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-04 00:02:17 +00:00
e1e3692fbc82d50c021ef8bf8ca7019d760705f0
407
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e1e3692fbc |
feat: data table roles in the DB manager and raw apps (#11139)
* feat(datatables): put a data table's connection under Postgres roles A data table backed by the instance database resolved to exactly one Postgres connection, `custom_instance_user`, for everyone who could reach it at all. There was no way to say this job reads, that one writes, this one never sees the salaries table. A data table role is now a real Postgres login on the cluster, defined once for the instance by a superadmin and named exactly as they named it. A script that declares `-- role analytics` connects as `analytics`, and Postgres decides what it may touch — grants are ordinary SQL. Windmill answers only "may this caller ask for this role", from the tenant lists on the data table entry: `u/alice`, `g/analysts`, `f/finance` or `*`. A data table with no `permissions` block behaves exactly as before. Everything that opens a connection on someone's behalf goes through one chokepoint, `get_datatable_resource_from_db`, which takes the identity explicitly and fails closed when there is none. The role logs in as itself — never `SET ROLE`, which a script could `RESET ROLE` its way out of. A fork's data table entry becomes a pointer at the workspace that governs it rather than a copy of it. The settings clone used to hand a fork a byte-identical entry naming the parent's database, which a fork admin could edit to grant themselves `admin` there; a pointer has nothing local to edit, and its tenants are evaluated as a member of the governing workspace, by email. `permissions` is stripped from the workspace export and ignored on import: tenants name principals of one workspace, and a settings push is not where an access decision should be made. Operations that see the whole database whatever the roles grant stay with the governing workspace's admins: editing the roles, a migration that declares none, and opening a replication stream for a Postgres trigger or capture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): gate the paths that reach a whole database as admin Auditing what still resolved through the unchecked resolver turned up three that act for a caller and hand back the admin connection: `resolve_pg_source_checked` (behind schema export, the full-schema read, database creation, import and the forked-database drop), the connection test, and the schema snapshot a fork clone takes of its parent. On a data table under roles each let any workspace member — or a fork admin who is nobody in the governing workspace — read or copy the whole database whatever its roles grant. All three now require admin reach on the governing workspace. A dump taken under a restricted role would be a silently truncated copy rather than an error, so refusing is the only right answer for the copy paths. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): confine roles to the instance database, and stop a fork reaching the parent's bookkeeping A data table role is a login on Windmill's own Postgres. Nothing stopped a workspace admin putting a *resource-backed* data table under roles, at which point the executor dialled the host that resource names — one the admin chose — with the role's real cluster password, and `CONNECT` is granted to every registered instance database. Both ends now refuse: the permissions endpoint rejects the save, and the chokepoint refuses to substitute credentials on a non-instance entry rather than trusting the record it read. Two more places reached the governing database without answering to it. The initial-migration generator returned a `pg_dump` of the whole schema to any member. And the migration rename/delete cascade followed a fork's pointer into the parent, so a fork admin renaming or removing their own local entry relabelled or wiped the parent's `_wm_migrations` — after which the parent re-runs every migration from zero. The remote half is now skipped when the entry resolves into another workspace, which is also just correct: a fork renaming what it calls a data table changes nothing about the data table. Also: revoking a tenant now bounces the replication streams of every workspace holding an entry that resolves here, not only the governing one, so a fork's trigger stops rather than living on inside its open connection; the instance role catalog and the governing workspace's tenant lists are no longer returned to someone who cannot edit them; and the tenant rename dedup collapses non-adjacent duplicates, per role rather than once any role changed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): fail loudly where a role or a pointer can be left half-recorded Three ways the feature could end up in a state nobody could see or undo. Creating a role writes the cluster first and the catalog second, but the catalog write was an `UPDATE` that matched nothing when the instance Postgres settings row was absent — leaving a live login with a password nobody recorded: invisible to the catalog, un-recreatable because the name is taken, and un-deletable because there is no entry to delete. It now errors, so the operation is retryable once the row is restored. Deleting a workspace only nulls the fork lineage; the data table entries pointing at it are left resolving to nothing. Sweeping them is not an option — turning a pointer back into a copy would hand each fork the database outright — so the delete now names the data tables it stranded, and resolving one says which workspace is missing rather than reporting a data table this workspace never had. `InstanceDatatableRole` derived `Debug` while holding a Postgres password; it is now hand-written so `{:?}` on the catalog cannot put a live credential in a log line. Adds the two branches the reviews found unpinned: a caller who is not a member of the governing workspace at all, and `NoIdentity` — the compatibility path for an agent worker that predates this and sends no job id. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): unbreak two operator messages and two comments that described other code The two strings this branch added for states an operator hits once — the catalog write that matched nothing, and the delete that stranded a pointer — were collapsed from their multi-line form with the indentation left in, so both rendered with a fourteen-space gap mid-sentence. `list_datatables` claimed to report a chain it cannot follow and then dropped it; it does drop it, and the comment now says why that is the right place to stay quiet. The non-superadmin check in `edit_datatable_config` was introduced as also covering references, which it does not and need not: `reference` is overwritten from the stored entry for every caller before the check runs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): serialize role catalog mutations, and state each helper's authorization contract The catalog is one JSON document, so create, rename, enable and delete are all read-modify-write. Two concurrent creates read the same snapshot, both succeed in the cluster, and the second write drops the first — leaving a live Postgres login with a password nobody recorded, which is the exact state the delete path exists to prevent. Every mutation now runs in one transaction holding an advisory lock across the read, the cluster DDL and the write, so a lost update cannot happen and a failure rolls the whole thing back. The DDL helpers take that transaction rather than the pool, which is what makes the lock cover them. Their statements moved off `sqlx::raw_sql`: the simple protocol is only needed for genuinely multi-statement SQL, and its future is not `Send`, which an axum handler holding the transaction requires. Each of these is one statement anyway. The new cross-crate surface now says what callers must do. `read_role_catalog` returns plaintext credentials; `create`/`rename`/`set_login`/`drop_instance_role` and `converge_connect_grants` mutate cluster-wide state; `read_datatable_entry` reads a workspace's raw config. All of them are superadmin-gated by their current handlers, but nothing said so at the definition, which is where the next caller looks. Also: the roles table reloads after a failed login toggle instead of leaving it claiming a flip that did not land; the rename affordance is the design-system `Button`, not a raw one; and `resolve_datatable_pg_as_caller` drops a `role` parameter no caller ever filled — browsing resolves as the data table's default until the database manager grows a picker. Why role passwords stay a plain `String` while the instance user's password beside them is a `StringOrSecretRef`, asked three times across reviews: that one is a secret ref because an operator supplies it and may want it from their own backend, while these are minted here and never entered by anyone, so there is nothing for a ref to point at. Encrypting generated secrets at rest is a separate change that would take the replication password with it. Now said at the field. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): give the role catalog its own row, out of reach of the config machinery Putting it inside `custom_instance_pg_databases` was the wrong call, and it cost two ways. The catalog serializes a generated Postgres password per role, and that row is the operator-facing instance config, so the passwords reached `get_instance_config` and its YAML editor — a live cluster credential in a response body, a UI field and any log of either. Worse in the other direction: `to_settings_map` strips the catalog, so a full-row upsert of that key writes the row back without it and the catalog is gone, while the cluster keeps every login it described. `custom_instance_replication_pwd` is the precedent and says exactly why — a generated secret, written only by the server, never operator-authored, hidden so the config machinery cannot read, rewrite or drop it. The catalog is the same thing, so it now has the same shape: `datatable_roles`, in `HIDDEN_SETTINGS`, `PROTECTED_SETTINGS` and the agent-worker denylist. No redaction to keep in step with three code paths, and no way for a neighbouring write to take it out. Two races on the same shared documents. `edit_datatable_config` read the stored data tables outside its transaction and then wrote the whole `datatable` document, so a permissions save committing in between was silently rolled back; it now reads under `FOR UPDATE`. And `set_datatable_permissions` validated role ids against the catalog before opening its transaction, so a deletion in between let it write a deleted role back — including as the default, which every later job then fails on; it now holds the catalog lock and the settings row across validation and write. Completes the authorization contracts the previous commit claimed but did not finish: `read_datatable_entry` (which it named and missed), `resolve_governing_datatable`, whose whole job is to answer for a workspace the caller may not belong to, and `converge_connect_grants_with`, which had not inherited its wrapper's. Also the generic Python SDK reference: `_format_py_params` learned the bare `*` last time, but `extract_py_functions` is a second formatter and still rendered `datatable(name, role)`, so code written from that page passed a keyword-only argument positionally. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): make the concurrency test pin the handlers, and the contracts describe what is enforced The concurrency test reimplemented the read-modify-write inline, so deleting the lock from all three handlers left it green — it pinned Postgres, not the code it was written for. It now drives `create_datatable_role` twice concurrently and asserts the catalog kept both names. Checked the way the last one should have been: removing the lock from the handler makes it fail with "wmtest_a_… is a live cluster login the catalog forgot". The contracts added last commit were stricter than this PR's own callers, which is worse than none — the next reader sees a rule already broken and learns to ignore it. `read_role_catalog` said superadmin-only while two of its four callers are open to any workspace member, and `converge_connect_grants` said superadmin while `set_datatable_permissions` reaches it as a workspace admin. Both were fine on substance: the rule that actually holds is about the credential never reaching a response, log, audit record or export, not about who may call. They now say that. `read_datatable_entry` gets the same treatment rather than the one the earlier message claimed for it: it is the primitive every resolution goes through, so it is deliberately open, and what must not escape is `permissions` — it names the governing workspace's users, groups and folders. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): close the last ways a role or a pointer can be left pointing at nothing The raw settings readers hand back whatever is in the row, so moving the catalog into its own `global_settings` key protected the config machinery and left `GET /settings/global/datatable_roles` and the settings listing returning every live password. Both now filter that one key. The neighbouring `custom_instance_replication_pwd` has the same shape and is not touched here: it predates this and widening the fix to it is a decision about an operator workflow, not a consequence of this change. Three ways a save could leave something resolving to nothing: A permissioned data table could be moved to a PostgreSQL resource. The block was carried across as a server-owned field, the runtime refuses roles on a resource-backed table, so the save succeeded and every job afterwards failed. Refused instead — turning roles off first is one step, and it keeps discarding an access decision something somebody chose. Renaming a governing data table left every fork pointing at the old name: the data table disappears from their pickers and their jobs stop, with nothing in the renaming workspace to suggest why. The rename now follows into the pointers in the same transaction. Deleting one cannot be followed the same way, so it is reported instead — the response names what it stranded, the way deleting a workspace does, and the fork's own error already says which workspace is gone. Also: `ensure_instance_db_grant_options_unchecked` claimed superadmin while the permissions handler reaches it as a workspace admin (the same class fixed last commit, one instance missed); the role entry kept an `instance_config_schema` derive it no longer needs; `write_role_catalog` was the one writer of that table not stamping `updated_at`; and the concurrency test dropped its roles only on success — a failing run is exactly the one that creates them without recording them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * refactor(datatables): put the role catalog in its own table, not in global_settings Five findings across three rounds were all the same choice. A set of live Postgres credentials was living in `global_settings`, which has generic read, list, write, config-export and CLI round-trip paths that know nothing about what they carry: the passwords reached the instance config and its YAML editor, a full-row upsert of a neighbouring key erased the catalog, `GET /settings/global/{key}` and the settings listing returned them raw, and this round the redaction that fixed the last two turned `wmill instance push` into something that wipes every password — a fix breaking the assumption the previous fix made. `POST /settings/global/datatable_roles` could also empty it outside the lock. The approved plan offered a table or `global_settings`, so this is the other option it already allowed rather than a new design. `datatable_role` is a table: no generic settings path can read it, list it, export it, write it or round-trip it, so none of the five needs a guard. The redaction, the hidden/protected/agent-denylist entries and the JSON document all go with it. One row per role also removes the read-modify-write the concurrency work was about: two concurrent creates are two inserts, and the unique index on `name` is what settles a collision. The advisory lock stays for the one window rows do not cover — `CREATE ROLE` is invisible to another transaction until commit, so without it both creates pass their `pg_roles` check. Also from this round: rename mappings are checked against the configuration they claim to describe, since fork pointers are rewritten from them — a caller could otherwise submit `main -> missing` against an unchanged config and repoint every fork of `main` at a name nothing has, and `A -> B` plus `B -> C` moved what pointed at `A` all the way to `C`. And the warning naming forks a delete stranded reached the response but not the screen: both the data table settings save and the workspace delete now show it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): validate a rename against the save it describes, and re-check under the locks Three from the round, all about deciding on state that could already have moved. A permission save resolved the data table and checked it was instance-backed before taking any lock, then wrote under one. A config save committing in between could move the table onto a PostgreSQL resource — recreating exactly what the transition guard refuses — or rename it, in which case the write targeted a key that no longer existed and reported success having changed nothing. It now re-resolves and re-checks on the locked state. Rename validation checked that the source existed before and the target existed after, which still accepts `main -> decoy` against a save that keeps both: every fork of `main` then follows onto a different data table, silently, because it keeps resolving. The rule is now the actual old-to-new key transition — a source may only survive if another rename took its name, and a target may only pre-exist if another rename freed it. That also stops two sources sharing one target, and it admits a swap, which the previous guard refused: `datatables` is keyed by name, so a swap cannot be done one save at a time, and refusing it was a regression against main. The pointer cascade now runs in two passes through a temporary name, the way the migration cascade one layer down already handles the same shape, so `A -> B` with `B -> C` moves each pointer once from what it named before the save. The tenant mutators say what they are for: they write an access decision for any workspace named, with an arbitrary mutation, and exist for the transaction that frees or renames a principal. Editing a decision on purpose belongs in the permissions endpoint. Carried in the same change: the stranded-fork list is a field rather than a phrase to grep out of a success string; the pointer cascade matches with `EXISTS` instead of a `LIKE` over the whole document, so a workspace whose pointers name something else is not rewritten to a byte-identical value under an exclusive lock; and `InstanceDatatableRole` drops the serde derives left over from the JSON document, one of which would emit `pwd`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): cascade on the leave route that is used, gate migrations before the admin connection, and drop a role atomically The tenant cascade on leaving went onto `/users/leave`. The UI and the generated client call `/workspaces/leave` — a different handler in a different crate with the same name — which deleted the membership and left `u/<username>` in the tenant lists. Leaving and rejoining therefore restored the access the leave was supposed to end, and a later account taking the username would have inherited it. The regression test drives the route the client actually calls; without the fix it fails with "leaving kept the tenant". The migration endpoints authorized too late. `run_datatable_migrations` opened the data table's admin connection, created `_wm_migrations` and read it before reaching the per-migration role check — so with nothing pending, nothing was checked at all. Rollback returned before its check when nothing was applied, and the status endpoint had none. All three now ask, before any connection is opened, whether the caller can reach the data table as any role at all; which role a given migration runs as is still decided per migration, and by the executor after that. Deleting a role committed the cluster drop and the catalog row, then swept the tenant lists in separate transactions. A sweep failing part-way left workspaces naming a role nothing can connect as, while the retry answered `NotFound` because the catalog entry was already gone. The sweep now runs in the same transaction, so the drop, the row and every tenant list commit together. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): refuse to copy a data table that is under roles pg_dump carries no roles and the import runs with --no-privileges, so a copied data table arrives owned by the admin connection with no GRANT for any role. The settings clone brings `permissions` across, so the fork's tenants pass Windmill's check, connect as the role they were given, and are denied by Postgres on everything: an entry that reads as configured and answers nothing. Refuse the copy — in the import endpoint before any data moves, and in the fork path the CLI takes. Replaying the source's owners and ACLs into the clone is what lifts this, and is a change of its own. Dropping `permissions` from the copy instead would be the unsafe half, since the copy holds the parent's rows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): refuse the clone's database too, not only its data A clone is two endpoints: `create_pg_database` then `import_pg_database`. Only the second refused a data table under roles, so a fork asking to clone one created and registered an empty `wm_fork_…` instance database and then failed — and nothing collects it, since `drop_forked_datatable_databases` only drops entries carrying `forked_from` and no entry names this one. Refuse in both, so the clone stops before a database exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * nit worker error msg * fix pg_dump stuck on version 17 on nix * fix(datatables): refuse a malformed role annotation instead of ignoring it `-- Role operator`, `-- role operator;` and `-- role operator -- why` all failed the annotation parser's exact-match rule, so the query fell through to the data table's default role and ran, silently, under a login the author did not choose. Naming a role exists precisely to not do that. A leading comment whose first word is `role` is now an annotation attempt: the keyword matches case-insensitively, one trailing `;` is tolerated, and anything else is an error naming the line. Only callers that already know the target is a `datatable://` reference ever run this, so ordinary SQL keeps its comments. Also bumps the dev shell's postgres client to 18 — it trailed the server the dev database runs, which takes out every data table export, clone and fork-with-data. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): refuse a malformed role query string instead of ignoring it `?Role=analytics`, `?role=` and `?x=1&role=…` all fell through the reference parser's exact-match rule, so the connection resolved to the data table's default role and ran under a login the caller never asked for — the URI half of the same trap as a malformed `-- role` annotation. The key now matches case-insensitively, and anything else in the query string is an error naming it; `role` is the only parameter a reference takes. Callers that only need the entry keep a lenient `datatable_ref_name`, since they never act on the role. The DuckDB `ATTACH` parser propagates it rather than attaching under the default. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): carry the role annotation into the row_to_json retry The retry rebuilds its SQL from `pruneComments(code)`, so the leading comment block never reached the second attempt — and with it the `-- role <name>` line that decides which login the query runs as. The retry connected as the data table's default role instead, so a query the first attempt was denied could succeed on the second, reported as "recovered with the row_to_json fix". Carry the leading comment block over. The retry itself is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * chore(datatables): don't mount the roles UI until the ACL editor lands Enforcement ships first. The permissions drawer is what turns roles on, and the catalog section is what creates them — both are only useful once there is a way to grant a role the privileges it needs, which arrives with the ACL editor. Left mounted they would offer a feature whose other half does not exist. The two components are complete and reviewed; only their call sites here are commented out, with a note pointing the follow-up PRs at them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * fix(datatables): honour `-- role: x`, and fix the DuckDB attach test Two review findings, both real. `attach_datatable_parses_name_and_role` never compiled: `parse_attach_datatable` returns `Result<Option<_>>` now and one call site kept a single `unwrap`. Its `?Role=analytics` case also asserted a refusal, contradicting the parser in the same commit, which matches the key case-insensitively. Replaced with the cases that are genuinely malformed, and a positive one for the cased key. `-- role: analytics` fell through to the default role — the silent fallback the strict parser exists to remove, for the spelling most likely to be typed. The keyword now accepts an optional colon, attached or spaced, while a word that merely starts with it (`rolebased`) is still not an attempt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * fix(datatables): clone a fork's pointer instead of failing after the copy Forking a fork with cloning left an orphan database. The preflight resolves the pointer and sees the governing entry, so both endpoints ran and filled the new database; `apply_forked_datatable` then refused the inherited pointer and rolled the fork back, stranding a registered `wm_fork_*` that no entry names and whose name blocks the retry. Refusing earlier would have been the smaller change, but forking a fork and cloning worked before pointers existed, so it would trade an orphan for a regression. Resolve what the pointer names and write the terminal entry the clone needs: the whole `database` object rather than a patch of its `resource_path`, since a pointer has none, and `reference` removed with it. Also accepts `-- role=x` and `-- Role = x`, two more spellings that fell through to the default role. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * fix(datatables): refuse to roll back the catalog while roles exist The down migration dropped the table and left every role behind: live Postgres logins whose passwords only that table carried, so after a revert Windmill could neither use, disable nor delete them, and re-applying could not recreate them because the names were taken. Cleaning up here is not possible either — dropping a role means reassigning what it owns in every instance database, and a migration runs in one — so it now refuses while the catalog is non-empty and says to delete the roles through instance settings, which does the cluster work. Also enforces the instance-only invariant the resolved-pointer clone relies on rather than only asserting it in a comment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * refactor(datatables): settle clonability in one place, before anything is created A clone is three stages a workspace apart — `create_pg_database`, then `import_pg_database`, then `apply_forked_datatable` inside the fork transaction. Only the third can roll back, and `CREATE DATABASE` is not transactional, so any refusal that lives there strands a registered `wm_fork_*` that no entry names and whose name blocks the retry. That orphan has now been fixed three times, most recently reintroduced by a guard added one commit ago. Patching each new refusal into the first endpoint is not the fix; having two places that can refuse is. `ensure_datatable_is_clonable` now answers every reason a copy can be refused and returns what it resolved, and the stage that writes the entry only does the work. Also takes an ACCESS EXCLUSIVE lock before the rollback guard counts, so a role created concurrently cannot slip between the check and the drop. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * fix(datatables): let a retried clone reclaim its own leftover database A clone creates its target database one request before it copies into it, and the fork that would name it is written a request after that. Any failure in between — a pg_dump error, a bad restore, a dropped connection, the source's roles changing mid-flow — left a registered `wm_fork_*` that no entry names, and every retry then failed on its name. This predates data table roles. `create_pg_database` now reclaims such a leftover before creating: only a `wm_fork_*` database Windmill registered as a data table database and that no data table or ducklake entry names, in any workspace, archived ones included. The drop never terminates connections, so a clone still copying into it makes the reclaim fail instead of being cut off. It is limited to callers who administer the source — reaching it is not enough, since on a data table without roles every member reaches it — and anyone else gets the refusal an existing database always got. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Revert "fix(datatables): let a retried clone reclaim its own leftover database" This reverts commit |
||
|
|
85b36a2de5 |
feat(ai-chat): disclose an app's exposure on deploy, refuse drag-and-drop apps (#11137)
* feat(ai-chat): disclose an app's exposure on deploy, refuse drag-and-drop apps Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XFF7zKcuVqzvKDBTMiWHk * refactor(ai-chat): leave the app kind check at deploy to the server Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XFF7zKcuVqzvKDBTMiWHk * feat(ai-chat): disclose a guest deploy where the instance admits guests Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XFF7zKcuVqzvKDBTMiWHk * test(ai-chat): pin each guest switch in the deploy disclosure Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XFF7zKcuVqzvKDBTMiWHk * docs(ai-chat): describe what a staged draft over a low-code app actually breaks Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XFF7zKcuVqzvKDBTMiWHk * feat(ai-chat): report who may open an app when reading it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XFF7zKcuVqzvKDBTMiWHk * fix(ai-chat): report a guest app as inert where no guest is admitted Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XFF7zKcuVqzvKDBTMiWHk * fix(ai-chat): report who may open an app that has a draft over it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XFF7zKcuVqzvKDBTMiWHk * docs(ai-chat): say which mode a drafted app read reports Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XFF7zKcuVqzvKDBTMiWHk --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
6186a0645d |
feat: edit variables, resources and triggers in their own session tab (#11206)
* feat: edit variables, resources and triggers in their own session tab Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: take a trigger's flow kind from its config and name the written trigger Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: one operating-workspace context for editors acting on a session's workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: reload page item tabs only in sessions acting on the tool's workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: resolve open in workspace for every preview tab kind in one place Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: guard every component under a session editor against the navigation workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: show a page item tab whose item is gone instead of mounting its editor Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: follow a saved page item in the tab's own workspace and restore in-frame rows Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: close a page item tab from its editor header Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: judge trigger permissions by the user acting in the session workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: judge every session-editor permission by the user acting in that workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep session permissions reactive to the acting role and restore tabs as last seen Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: default Path's owner controls to the user acting in its workspace 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> |
||
|
|
a08992834d |
feat: render an AI agent result as its answer, not as raw JSON (#11051)
* feat: render an AI agent result as its answer, not as raw JSON Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: sanitize agent markdown through the shared plugin chain Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: fold agent stream events incrementally per poll Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: separate the agent meta line from the result toggle group Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: add a transcript view of an agent run's conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: retire AIAgentLogViewer in favour of the transcript Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: show the partial transcript a max-iterations failure carries Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: move the agent meta line and system prompt below the conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: show an agent run as what it did, not as a conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: name the agent run breakdown a trace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): label the save-as-agent form fields per the guidelines Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): reuse the resource form's path and description fields Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): keep the action tags on a max-iterations failure Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): keep offline replay inert and the streamed answer to one turn Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): reset the streamed answer on providers that skip tool_call Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: carry the event type narrowing through the stream parser Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: survive a malformed message rather than take the viewer down Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: coerce agent messages once at the parse boundary Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: show an agent run as one scroll ending in its output Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep every streamed turn instead of dropping the narration Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: share the chat divider and drop the unsafe run auto-scroll Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: give the labelled divider a border colour and the standard pretty icon Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: pad the agent run below its badges as well as above Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: follow a streaming run's pane without moving the page Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: share the chat's stick-to-bottom mechanics with the agent run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the answer's citations and end a turn's reasoning with the turn Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: read the agent stream through the windmill-chat sdk parser Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: show an agent run's thinking instead of falling back to raw json Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop a stream the fold cannot use from claiming the run pane Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: identify an agent run by more than the job id the replay withholds Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: drop the tests and comment lines that were not earning their place Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop a streamed turn's text shifting when a tool call closes it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
c8c06d8f79 |
feat(ai-chat): tell the chat which kind of app it is looking at (#11208)
* feat(ai-chat): tell the chat which kind of app it is looking at Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018r5ppn8YmHsM1MaAwY7xgB * refactor(ai-chat): drop a stale comment and hoist the prefix local Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018r5ppn8YmHsM1MaAwY7xgB * test(ai-chat): add a global eval for refusing to edit a drag-and-drop app Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018r5ppn8YmHsM1MaAwY7xgB * test(ai-chat): make the app-kind eval require editing the code app too Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018r5ppn8YmHsM1MaAwY7xgB --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
6f9c4dc294 |
feat(ai-chat): render get_run through the run tools' card (#11204)
* feat(ai-chat): render get_run through the run tools' card Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-chat): retry unreadable logs on reopen and drop the loading tint Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(ai-chat): inline two single-use deriveds in the run card Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(ai-chat): record why an inspected run's logs need the dedicated endpoint Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-chat): key an inspected run's fetch to the tool call, not the job Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(ai-chat): say an inspected run's logs are a tail, not the whole log Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e2a91ca2b1 |
feat: flow chat job-backed detail, smooth streaming and answer chrome (#11186)
* feat: flow chat job-backed detail, smooth streaming and answer chrome * chore: document setFlowPath, drop unused chatIdentity, tighten comments * fix: read a nested agent tool's call from its own job * fix: show only the model's arguments on a tool card read from its job * feat: add attachments to the chat sdk message type * refactor: read a flow chat message's files from the message, not its run * fix: keep every job-backed answer when reads finish out of order * refactor: build flow chat tool cards from the conversation row alone * feat: keep the model picker's current choice when a flow chat retry replays its run * fix: export ChatAttachment from the chat sdk entry point * fix: drop a conversation list for the flow path before setFlowPath * fix: return the current listing, not old-path rows, after setFlowPath * fix: treat a listing stale on kind or path as one stale listing * revert: drop setFlowPath and rebuild the chat when the flow path changes * fix: refuse a flow chat retry once any turn ran while it read the run * docs: say where a display message's createdAt comes from * style: drop the rule between New chat and the conversation list |
||
|
|
c4c9677982 |
feat: attach files to a flow chat message (#11185)
* feat: attach files to a flow chat message Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: store chat uploads under windmill_uploads and withdraw refused sends cleanly Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: refuse extra files for a single-file input and keep attachments on retry Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: clean up partial upload batches, withdraw a stopped send once, free retry payloads Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: list a chat conversation only once its run starts and require files where the flow does Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: carry uploaded attachments on the pending chat user message Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: route rich-composer file paste through the attachment lanes and tighten comments Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep a stopped turn's files for retry and let an explicit media type win Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: refuse chat attachments sent without text and shorten comments Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: resolve the attachments input from real flow input reads, ignoring loop iteration Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: read flow input references through one parser for the model and attachments controls Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: withdraw an attachment send stopped after its uploads answered Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: discard uploads when a send is stopped as its attachments are announced Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: keep uploadAttachments' doc comment on uploadAttachments Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: never delete chat uploads from workspace storage Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: pin that a failed upload aborts the rest of its batch Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5371519f0f |
feat: let plan mode search and read connected mcp servers (#11205)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6e1ef93f32 |
feat: let test_run_flow name the conversation of a chat-mode test run (#11198)
* feat: let test_run_flow name the conversation of a chat-mode test run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: refuse a non-UUID conversation_id and mint chat test-run ids in one place Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(ai-evals): add a chat-flow follow-up case and mock flow preview runs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: expect the conversation id argument on the manager's flow test bridge Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(ai-evals): require the chat-flow follow-up runs to share one conversation id Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: name the test_run_flow argument memory_id after the run parameter Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
c297ed0052 |
feat: managed memory with an inherited or custom memory id per step (#11118)
* feat: split ai agent memory into agent policy, run memory id and step history * fix: scope string memory ids to workspace and flow, keep nested tool history inputs * chore: update sqlx cache for the flow context query * docs: describe memory id scoping as collision-free rather than isolated * chore: regenerate openflow json after merging main * fix: offer no memory id for legacy manual memory, document linked history inputs * fix: seed provided messages from legacy manual memory and hide its note once set * fix: bypass memory when a provided messages expression evaluates to null * fix: require a user message when provided messages are empty * chore: keep the empty messages comment within the line width * docs: name the history inputs wherever linked steps list their flow-local inputs * docs: keep the memory storage path on one line * feat: managed memory with an inherited or custom memory id per step Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: list a custom memory id in the test run form and name where an inherited one comes from Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep memory id out of the add-field menu and drop the memory id telemetry Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep legacy auto memory without an id working after an untouched redeploy Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: rename step messages to previous_messages and address review Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * style: rewrap comments and docs lines lengthened by the previous_messages rename Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: read agent memory as either a legacy shape or the current one Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: name the memory setting in ignored-input notes and keep conversions honest Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep a legacy memory count unset on open and read a cleared count as off Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: address review on cleared test history and zero-count memory Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: drop flow-local keys from a linked agent resource before interpolating it Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: keep a linked resource's own inputs as fallbacks and note ignored history on image runs Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: restore the linked agent draft tests and log ignored history on every image run Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: resolve the one-of variant from the value when the selected one leaves the list Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: treat zero-count managed memory as off when enabling chat mode and shorten comments Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: stop requiring user_message in the openflow agent contract when previous messages are the prompt Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e954d33613 |
feat(ai-chat): add list_workers and list_data_metrics global tools (#11143)
* feat(ai-chat): add list_workers and list_data_metrics global tools Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-evals): keep the generated eval workspace id valid for the backend Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(ai-chat): trim the worker truncation note and added comments Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq * fix(ai-chat): only hedge an empty worker list when workers can be hidden Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq * fix(ai-chat): show the empty-worker message in the tool card Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq * fix(ai-chat): stop advertising workers in the api catalog tool Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq * test(ai-evals): move the workspace id guard beside its function Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq * fix(ai-chat): keep the empty metric note true for every filter shape Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq * fix(ai-chat): resolve the caller role before judging an empty worker list Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq * fix(ai-evals): collapse doubled hyphens in the generated workspace id Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq * docs(ai-chat): describe what the ducklake tools and the worker hedge do Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq * fix(ai-chat): name the missing lake when a metric table filter has none Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq * fix(ai-chat): strip the ducklake scheme from the table retry hint Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq * test(ai-evals): add a global case for the declared measure path Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gi6UeLKJB9UaSWtjDCVaMq --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5bb37ca338 |
feat(ai-chat): merge get_job_logs and get_flow_run_details into get_run (#11172)
* feat(ai-chat): merge get_job_logs and get_flow_run_details into get_run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1arUSfbbVQSq2DsjamxKF * fix(ai-chat): tell an unreadable log apart from an empty one in get_run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1arUSfbbVQSq2DsjamxKF * fix(ai-chat): keep get_run's step tree optional when the read resolves undefined Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1arUSfbbVQSq2DsjamxKF * fix(ai-chat): match each WINDMILL_TOO_BIG marker to the field it elides Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1arUSfbbVQSq2DsjamxKF --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
189793c2e4 |
feat: flow chat model picker on a shared model-settings component (#11187)
* refactor: render the session chat model menu from a shared ChatModelSettings config Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat: pick the flow chat's model and thinking from the provider fields the flow exposes Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: name only the thinking level the flow run will send on the model button Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: let the flow chat take a typed model id and keep a shared thinking input editable Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: promote a flow input to the model button only where its control can edit it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: drop any reasoning token the chosen model rejects before a flow chat run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
a9ec0aec3a |
feat: render the flow chat through the shared session chat components (#11175)
* feat: render the flow chat through the shared session chat components Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: settle the flow chat queue on the turn's outcome and guard IME enter Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: keep copilot free-tier banners and deploy-locked sends out of the flow chat Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
a48ae656ae |
feat: delete a browser's copy of an AI session past its workspace retention (#11156)
* feat: delete a browser's copy of an AI session past its workspace retention Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: tell the AI session retention only to a member who can reach the workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: keep the retention sweep's design narrative in the docs, not the code * fix: give the session retention its own route, leaving the status contract alone Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: shorten the retention route comment to its constraints * docs: name the two clocks in the retention setting, and the deploy window --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
796b6e5297 |
feat: back AI sessions up to the workspace object storage (#11116)
* feat: back AI sessions up to the workspace object storage Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SG5qEPM6Fmf7VerXS5nnWp * fix: bind the backup key to the user and pack pushes within the server caps Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SG5qEPM6Fmf7VerXS5nnWp * fix: keep refused and unavailable marks, one mark per key, stream the flush Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SG5qEPM6Fmf7VerXS5nnWp * fix: settle only fully sent sessions, keep removals while backups are off, cap pull bodies Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SG5qEPM6Fmf7VerXS5nnWp * fix: bound removal marks while backups are off and stale the sync rows instead of dropping them Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: retry a lost lock, cap nested push lists and oversized pieces, drop a stale copy of a chat that outgrew the backup Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: cap pieces per push, size requests in UTF-8, keep a move's removal for an off workspace, disclose the restore counter Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: file a move's removal only once the new copy landed, retire marks through the sync row Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: keep a session marked while deletes are carried over, drop only gone sessions' marks Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: record what a refused flush already stored, stop early when every mark is retired Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: restore past another workspace's removal mark, file a move's removal before its row, bound the first pulled session Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: re-key the backups on workspace key rotation, accept only base64 images, carry a delete on the sync row when its mark cannot be written Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: durable conditional re-key of session backups, re-push on a storage switch Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: fail a push the key rotated under, settle no session split across storages, narrow the re-key module Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: keep a delete filed during a push, bound the pull and re-key listings Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: bound the session listing, mark the store's own user on a write that lands after a user switch Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: list sessions through per-session index markers, hold a session's parts back after a failed one Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: record a rotation on every build, list a session only on the part that completes its push Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: leave an object larger than any push writes unread Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: read each object against its listed size, carry a dirty mark that cannot be written on the sync row Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: note the storages the re-key walk completed on, reach another user's rows on a failed mark, read a head at its cap Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: read the replaced key under its row lock, carry a refused dirty bump on the sync row Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: pull a session that outgrew one answer in pages, imported only whole Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: build a pull page from the smallest keys of the whole listing, stage each page as it lands Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: re-record a key rotated back to, admit earlier-page images, restage over a cut-short restore Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor: delete the backups on key rotation instead of re-keying them Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: end a pull page before an object that grew since the listing, prune what a cut-short restore staged Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: delete the backups before the key commits, skip a planted object whatever its listing says, lock a restore across tabs, prune stale artifact versions Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: keep the backups under a prefix named by the key, delete the previous key's prefix after the commit Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: name the backup prefix by a generation the rotation bumps, never write an older record over a newer one on restore Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: retire a removal only against the storage holding the backup, restart a paged pull whose listing moved Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: fingerprint a pull page before reading it, answer the backup generation apart from the storage identity Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: answer needs_head for a headless session push, prune restaged pieces by id Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: serialize a session's push and removal, open whole pushes with the head, prune only own restores Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: require a head on a whole push, prune before the record lands, restore only under Web Locks Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: incremental pushes ride on a listed session, removals wait for every storage holding a copy Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: a whole push replaces the backup under a per-push token, a pull page is checked after its reads Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: fingerprint a pull page by entity tag and version too Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: a moved session's removal mark names the storages holding the old copy Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: restore a workspace family together, the newest copy of a moved session winning Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: name the marker by the session's move count, abort a family restore a listing failed in Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: list the family again before a restored record lands, require the pull fingerprint Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: list the whole family once per restored workspace, off members included Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: a push split over parts, incremental too, unlists the session until its last part Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: refuse a partial push part that names no push Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: keep a refused bump for a session with no row yet, probe an off workspace again Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: backfill row-carried bumps after a reload, ask an off workspace again on a timer Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: backfill a row for its bumps only when it carries some Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: a backfilled mark that cannot be written counts from the page's counter Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: say an off workspace is asked again, in the mirror's comments Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore: update ee-repo-ref to 1c1dab33563c4907aff8b0da825fb66db60af82a This commit updates the EE repository reference after PR #796 was merged in windmill-ee-private. Previous ee-repo-ref: 289b477ca3fc993da06ec09b11c8f55d5e4e39c1 New ee-repo-ref: 1c1dab33563c4907aff8b0da825fb66db60af82a Automated by sync-ee-ref workflow. * fix: unlist a session while an incremental push changes more than one object Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
a78beff743 |
feat: dynamic AI agent toolsets (#11050)
* feat: dynamic ai agent toolsets, and memory as a step input Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review round 1 on dynamic ai agent toolsets Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: tag enabled_tools and drop the memory step input Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: let an mcp server entry be named by the path the roster shows Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep $res: out of the tool names the enabled tools picker offers Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: name an mcp server by its bare path on the one side that can hold it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: count the enabled tool names that matched nothing instead of logging them Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: narrow an agent's roster in one pass, by whole entries Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: pin that an mcp summary is rejected against a name that is not Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: regenerate the copilot flow schema after the merge Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: shorten the enabled tools list hint Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: take enabled_tools back to a plain list of tool names Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: keep the enabled tools add-menu hint describing the unset field Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: name a websearch tool that carries no summary of its own Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: reserve the name web search is enabled by so no tool can share it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: spell the reserved web search name with a hyphen Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: reserve __wm_web_search as the name web search is enabled by Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: advance ee-repo-ref past the git sync ci check work Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: shorten the enabled tools description the run form shows Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to e4c1b794d6c5e6e390987341b2840587bbb40348 This commit updates the EE repository reference after PR #785 was merged in windmill-ee-private. Previous ee-repo-ref: af668462f0f06b02a5f4e0c22e6156858487a518 New ee-repo-ref: e4c1b794d6c5e6e390987341b2840587bbb40348 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
57a134e2de |
feat(ai-sessions): share session artifacts with the workspace by link (#11115)
* feat(ai-sessions): share session artifacts with the workspace by link Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pyjp67oR269QAx3b4yf4oH * chore: cache the shared artifact queries for offline sqlx Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pyjp67oR269QAx3b4yf4oH * fix: replace a literal NUL byte in the shared artifact body limit comment Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pyjp67oR269QAx3b4yf4oH * test: pin that a shared artifact is confined to its workspace's path Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pyjp67oR269QAx3b4yf4oH * fix: sanitize shared artifact markdown and validate the artifact id on every route The shared page renders another member's markdown, so ArtifactBody now runs the repo's rehype-raw + rehype-sanitize chain with the chat's link renderer on top; only the session viewer opts into the chat code block (mermaid, apply button). The link renderer keeps a link's text when its href is empty or unsafe, and the scheme check moves to a tested helper. The status route checks artifact_id like share does, so a NUL is a 400 rather than a 500. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pyjp67oR269QAx3b4yf4oH * fix(ai-sessions): say which way re-sharing moves an artifact link The popover offered "Update to v1" when a v2 link was open on a pinned v1, which reads as if v1 were newer. Each direction now has its own sentence and action: a newer version on screen updates the link, an older one shares that version instead, a rename updates the name. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5d32b61067 |
feat: run a flow step test through the chat's argument form (#11114)
test_run_step was the last run tool still starting a job on whatever the model sent. Route it through runThroughForm, as test_run_script, run_script and test_run_flow already are. A step's arguments are its own, not the flow's: it is normally fed by its input transforms, so the form is built from the step's target rather than the flow's schema. loadSchemaFromModule resolves script and subflow steps against the deployed version, which would offer the fields of code this path is not about to run, so the schema comes from the same read the job uses — inferred from a rawscript body, the draft script's content, or the subflow's own schema. The preprocessor's _ENTRYPOINT_OVERRIDE is declared by no schema, so it is added inside the resolved startJob: proposed into the form instead, the argument conforming would drop it and the preprocessor would silently run its main. Its schema is inferred rather than read off the target for the same reason a stored one cannot describe it: a schema speaks for the one entrypoint it was inferred from. executeFlowStepTestRun splits into resolveFlowStepRun plus a thin wrapper, so the flow editor's own test_run_step keeps its behaviour but for one fix it inherits: a deployed subflow step now runs with skipPreprocessor. The flow editor's step test passes it too, and a parent flow pushes a subflow step the same way (apply_preprocessor: false) — a preprocessor would take the subflow's own inputs for a trigger event. Claude-Session: https://claude.ai/code/session_018PBB2gw8FK4YmGPxu5Drbn Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
244ec13291 |
fix(ai-chat): hide other users' MCP servers from the chat unless shared (#11112)
* fix(ai-chat): hide other users' MCP servers from the chat unless shared An admin's database role lets the resource listing return every user's u/ MCP resource, so the chat's "+" menu and the assistant settings tab offered servers that carry someone else's credentials. Both lists, and the tool loader behind them, now keep a u/ server only when it belongs to the current user or its extra_perms name them or one of their groups. The Resources page is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0131DsyTiBR6Q54qPXbTX5sC * fix(ai-chat): resolve the MCP viewer for the workspace being listed Username and groups are per workspace, and a session chat can operate on a workspace other than the one being browsed, so the filter now takes its identity from that workspace's whoami rather than from userStore. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0131DsyTiBR6Q54qPXbTX5sC * fix(ai-chat): list the whole MCP catalog before filtering, one predicate The visibility filter runs after the server's LIMIT, so a 100-row page could drop the viewer's own servers behind foreign u/ rows. The three call sites now ask for 1000 and call the tested predicate directly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0131DsyTiBR6Q54qPXbTX5sC --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
56dd940e34 |
fix: keep a script draft's password marking through the chat's run form (#11110)
* fix: keep a script draft's password marking through the chat's run form Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dx9oMWHnKywoqooZBeaPCy * docs: describe what the inferArgs test mock actually lets the suite pin Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dx9oMWHnKywoqooZBeaPCy --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d8d7332eb6 |
feat: add per-route CORS origin allowlist for HTTP triggers (#10833)
* 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> |
||
|
|
b50de89479 |
feat: run a deployed flow through the chat's argument form (#11085)
* feat: run a deployed flow through the chat's argument form Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015vHs7Jr4UDSbUe2KGjugMw * refactor: drop the unread dynselect helper from the deployed flow run form Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015vHs7Jr4UDSbUe2KGjugMw * fix: skip the preprocessor when the chat runs a deployed flow Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015vHs7Jr4UDSbUe2KGjugMw * test: pin run_flow steering with ai_evals cases Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015vHs7Jr4UDSbUe2KGjugMw * refactor: inline the deployed flow schema and trim the eval draft check Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015vHs7Jr4UDSbUe2KGjugMw * docs: correct the stale draft-validation comment on the flow test-run eval Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015vHs7Jr4UDSbUe2KGjugMw --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e7c6f85553 |
feat: give the chat the full MCP tool schema, and mark calls with the provider icon (#11086)
* feat: mark MCP server lists and chat tool calls with the provider icon Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgzuxyafKNF2uaEeL35XQw * feat: return the full MCP tool schema from search_mcp_tools Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgzuxyafKNF2uaEeL35XQw * fix: bound an empty MCP search result and keep the server mark decorative Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgzuxyafKNF2uaEeL35XQw * fix: keep the more-matches hint and scope a marked row to its own workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgzuxyafKNF2uaEeL35XQw --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2939c2dd4b |
feat: background and wait_seconds for run_script, skip preprocessor (#11092)
Claude-Session: https://claude.ai/code/session_01YS9n9Mq5CER6bApudfKMdi Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d8b9174235 |
feat(ai-sessions): turn skills on by default, and group them by folder (#11058)
* feat(ai-sessions): turn skills on by default, and group them by folder A skill is instructions the workspace wrote for the assistant to use, so what carrying one costs is context rather than access. Selecting each one before it applied made publishing a skill a two-step affair, and left most of them unused. Skills now default to on. No storage is rewritten to get there: the preference keeps its key and holds a decision per path, so the older array of enabled paths still reads as "these were on" and only the paths nobody decided about move. MCP servers stay opt-in through the same factory — their tools reach an external system, which is a different question from context. The Skills settings list groups into a tree once skills span more than one folder, with a switch per folder acting on everything beneath it, and the list answers the keyboard: Up/Down walk it, Left/Right fold, Space flips the switch under the highlight, Enter opens the skill. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: give a modal the option to stand only as tall as the window `AIPromptsModal` asks for 1000px of height, which is taller than a laptop window: the dialog then scrolled inside the overlay while its list scrolled inside the dialog — two scrollbars, one of them moving the modal itself. The cap `Modal2` appeared to have, `max-h-screen-80`, is defined nowhere in the tailwind config, so it never applied to anything. `fixedHeight="viewport"` is a new value that stands as tall as the window allows. Deliberately a definite height rather than a max-height: bodies here size against the box with `h-full` / `grow min-h-0` and scroll inside it, and a max-height leaves them nothing to resolve against — they grow past the surface instead. Every existing size keeps the height it has today, so no other modal moves. The two classes that resolved to nothing are removed. The prompts modal and the assistant settings modal take the new value; both already scroll inside themselves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: pin read_skill's gate in its test, and say who a delete affects The refusal `read_skill` gives for a path that is not a skill changed shape — it checks the workspace listing now, not just the off-switch — and its test was still asserting the old wording against an unmocked listing. The delete confirmation said everyone "who selected it" loses the skill, which stopped being true when skills started defaulting to on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: keep the keyboard walk when the list scrolls under the pointer The mouse takes the skills list back on a real movement over it, not on `mouseenter`. The browser fires that one whenever rows arrive under a stationary pointer — every scroll the keyboard itself causes, and every folder collapse — so walking Down past the bottom of the list handed control back to a mouse nobody had touched, and the next press restarted at the top. Also from the review round: the "+" menu sorted skills on-first, a key that is constant now that they start on, and pushed the one row it did move — a skill just turned off there — out of the shortcut that turns it back on. It orders by path. The remaining "selection" wording follows the vocabulary the rest of this change moved to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: carry the keyboard walk on from the row the mouse left it on Handing the list to the mouse dropped the highlight, so the next arrow press started again at the top. It moves to the row under the pointer instead — invisible while the mouse leads, since drawing and acting both wait on the keyboard being in charge, and exactly where someone would expect the walk to carry on from. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: cap the AI prompts modal from its own call site Reverts `Modal2` and the assistant settings modal to what they were. The prompts modal asks for `xxl`, 1000px, which is taller than a laptop window, so the dialog scrolled inside the overlay while its list scrolled inside the dialog. It now passes `max-h-[80vh]` through the `css.popup` the component already forwards. The height stays definite underneath, which is what lets the list bound its own scroller, and nothing outside this one modal changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * refactor: drive the skills list highlight with useListHighlight The Tools section next door already had this: `useListHighlight` owns the highlighted index, wrapping, `scrollIntoView`, and the rule that a scroll under a resting pointer must not hand the list back to the mouse — the bug this section rediscovered the hard way. Reusing it drops the parallel implementation. What stays local is what is actually a tree: Left and Right fold a folder or step into it, Space flips the switch under the highlight, and Enter opens the lit skill. `restingIndex` is what keeps the highlight on a folder through a fold, where a search would instead send it back to its top hit. The keys are answered at the window rather than on the list: leaving the editor parks focus elsewhere, and a container-scoped handler goes silent when it does. `move` is now returned by the composable, for the step into a folder's children. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: stop the fold's sticky row resetting the keyboard walk `stickyKey` is read through `restingIndex`, which `useListHighlight` calls inside the effect that reacts to the row count. As `$state` it was also a dependency of that effect, so clearing it on the next arrow re-ran the effect and wrote the highlight back to nothing: after collapsing a folder, one Down lit nothing and the one after it started again at the top. It is a plain variable now, read when the effect runs and invalidating nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: keep one lit row, and keep the fold's sticky row to its fold Three from the review of the `useListHighlight` swap: The sticky row a fold takes is now given up as soon as that fold has rendered. Held until the next arrow, it pulled the highlight back to that folder on any later change — another fold, a save, a delete, a workspace switch. Space and Enter on a focused control bring the highlight to that control's row before the control answers them. A switch keeps focus after a plain click, and the row drawn as highlighted was then a different one from the row that flipped. Up and Down carry on from a row reached with Tab. `useListHighlight` cannot see that by itself: `ListRow` puts the row's id on its outer div while focus sits on the button inside it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: land the highlight on a named row rather than stepping to it `move` counts steps from wherever the highlight is, and from nothing lit it can only reach an end of the list — so the three places that meant "put it on this row" (a row reached with Tab, the row of a focused control, a folder's parent) sent it to the first row whenever nothing was lit yet. `useListHighlight` grows a `moveTo` for naming the row outright, and those three use it. The handler's own doc still said the keys are answered on the list; they went back to the window when the editor's page transition proved able to take focus away from it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: fold from the header click the way every other fold does The header's own click wrote `collapsed` directly instead of going through `fold`, so the row count changed with no row named to keep: the highlight reset, and since the highlight is the header's only hover feedback, it went flat under a pointer that had not moved and stayed flat. Also from the round: a duplicated `svelte-ignore`, the missing one on the header wrapper that takes `onmouseenter`, and a trailing comma prettier wanted gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
172d6c275b |
feat: run a flow test through the chat's argument form (#11069)
* feat: run a flow test through the chat's argument form Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MxWRaAbciaeRxam5gPzaaV * fix: resolve the flow editor to run when the form is submitted Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MxWRaAbciaeRxam5gPzaaV * refactor: trim the flow run form's duplication and narration Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MxWRaAbciaeRxam5gPzaaV * fix: label test_run_flow in the bypassed-tools tooltip Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MxWRaAbciaeRxam5gPzaaV * fix: show the flow icon on a flow's run form in the preview panel Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MxWRaAbciaeRxam5gPzaaV --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
63cb46d7bb |
feat: add a minimal skin for the approval page and slack/teams (#11061)
* feat: add an approval skin to the approval page and slack/teams messages Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KK2Ye4PizykReZVuMEm3m9 * chore: point ee-repo-ref at the teams approval skin commit Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KK2Ye4PizykReZVuMEm3m9 * fix: resolve the approval skin from the step awaiting approval Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KK2Ye4PizykReZVuMEm3m9 * fix: shorten the slack approval message to fit the button value limit Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KK2Ye4PizykReZVuMEm3m9 * fix: rename skins to detailed/minimal and keep long slack messages Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KK2Ye4PizykReZVuMEm3m9 * feat: title the minimal approval page from the step and flow summaries Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KK2Ye4PizykReZVuMEm3m9 * feat: let wait_for_approval set the description approvers see Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KK2Ye4PizykReZVuMEm3m9 * fix: keep a finished workflow's approval description, still gated Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KK2Ye4PizykReZVuMEm3m9 * fix: keep a login-required approval locked after the run moves on Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KK2Ye4PizykReZVuMEm3m9 * feat: hide the windmill version on the approval page Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KK2Ye4PizykReZVuMEm3m9 * chore: update ee-repo-ref to e92abc9d1fba3ba898640df0cfeb0af8a50849b4 This commit updates the EE repository reference after PR #786 was merged in windmill-ee-private. Previous ee-repo-ref: bf1766ff49458f62d3f11746f1a06893ae3c2325 New ee-repo-ref: e92abc9d1fba3ba898640df0cfeb0af8a50849b4 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> |
||
|
|
fa73539839 |
fix(ai-chat): test_run_flow could test a different flow than the one asked (#11066)
* fix(ai-chat): test_run_flow could test a different flow than the one asked Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmQDoFXVPwYyEN8PfV2oL7 * fix(ai-chat): prefer the flow editor stored at the path over one renamed to it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmQDoFXVPwYyEN8PfV2oL7 * test(ai-chat): default the flow helpers factory and trim duplicated setup Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmQDoFXVPwYyEN8PfV2oL7 * refactor(ai-chat): resolve the flow editor to run by its storage path Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmQDoFXVPwYyEN8PfV2oL7 * refactor(ai-chat): move the editor storage path context out of sessions Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmQDoFXVPwYyEN8PfV2oL7 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a6abf2c8a7 |
feat: run and test scripts from the AI chat through an argument form (#11001)
* fix: disable a dynamic input when its schema field is disabled Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * refactor: extract the run form's argument hygiene into job_args Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: give Tabs an opt-in sliding selection indicator Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * refactor: share the chat's scroll-fade measurement Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: give the chat a run-form contract and incremental job output Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: run and test a script from the chat through an argument form Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: carry a chat run's card and job across saves and reloads Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: render a chat run as a tool call row with its form, logs and result Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: open a pending run form in the sessions preview pane Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * test: benchmark running a deployed script from the chat Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: offer a test run's dynamic options from the draft it previews Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: say that a test run's dynselect helper executes on form display * fix: send a schema default the model omitted when yolo skips the form * fix: infer a test run's schema when the stored one declares no properties * docs: tighten the note on the form's mount-time helper job * fix: apply a nested schema default the bypass posture counts as answered * fix: apply a declared default to a null value and an optional nested field * fix: check required fields inside a supplied optional object before bypassing * fix: read required args as own properties before bypassing the form * fix: stop the turn from the run form's action row in the preview panel * refactor: drop the run-form prediction and share its secret minting * refactor: prefill a proposed secret instead of emptying the field * docs: correct the comments the run-form prediction left behind * fix: keep a proposed secret out of the chat's stored messages * docs: say what a literal secret argument now does * test: restore the copilotInfo export the aiStore mock omits * docs: cut the run form's helper-script note to its constraints * refactor: settle a run form from one entry and fetch a job's logs once * fix: separate colliding secret paths, gate plan mode, keep polled logs * fix: mint before the form opens, skip empty fields, show what ran * revert: mint a run form's secrets at submit, not before it opens * fix: settle a cancelled run card on the form's arguments, not the proposal * fix: settle a stopped run form like a cancelled one, and keep an empty secret empty * fix: snapshot a run's arguments before minting its secrets --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c6e0302d7c |
feat: let // materialize declare a dbt:// warehouse-relation write (#10978)
* feat: let `// materialize` declare a `dbt://` warehouse-relation write `// materialize manual dbt://<warehouse>/<schema>/<name>` lets an ingestion script in any language declare that it writes a warehouse relation, so it and the dbt model reading that relation land on one asset node instead of two disconnected pictures. `manual` is the only mode a warehouse target has — nothing generates warehouse DDL — and the non-`manual` spelling is refused rather than silently degraded. The `<warehouse>` segment is resolved against the workspace's configured warehouses, like a descriptor's `profile.warehouse`. The run records the same `materialized_partition` row a DuckLake target does, from the generic job path rather than an executor: the DuckLake write engine is DuckDB's, this declaration is anyone's. With a non-dbt producer now possible, the blanket deploy-time refusal of `# on dbt://<relation>` narrows to the shape that still cannot fire — every writer of the relation being a dbt script, since a dbt run does not dispatch. "Nothing produces it yet" stays accepted, as for every other asset kind, so deploy order does not matter. A dbt script may not subscribe at all: its graph ingest clears its own `dbt://` trigger rows. The one ordering the deploy cannot catch — a subscription accepted before any producer, then claimed by a dbt project — is named in that project's deploy log. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rw1WrKeRRzyYHjfkuB83ek * fix: address review — preview stamping, stale producer set, public doc Three findings from the local review round: - Record the warehouse write only for a DEPLOYED script job. The annotation is a deploy-time contract (`manual`, three segments, a configured warehouse) checked where write access to the path is also required; honouring it in a preview, hub or inline-flow body let `jobs:run` alone restamp any relation's last writer from a script that never touched it. - Exclude the deploying script's own rows from the producer set. Read committed, they describe the version being replaced, so a script dropping its `// materialize` while adding a subscription counted itself as the producer that would wake it and committed a dormant edge. It could not be that producer anyway — the dispatcher skips self-loops. - `AssetKind::Dbt`'s doc no longer claims dbt is the exclusive producer of a warehouse relation, on both the types and the parser enum. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: review round 1 — dbt-script materialize, set-form rule, doc - Refuse `// materialize` on a dbt script, the producer half of the rule the trigger loop already applies to `// on`: the graph ingest republishes that path's asset rows wholesale, so a declared write is wiped by the deploy that accepted it while its runs keep stamping the relation. - `dormant_dbt_subscriptions` now spells the same predicate its singular sibling does: the producer set has to be non-empty (nothing produces it yet is deploy order, not a dormant edge) and excludes the subscriber's own path (a script never wakes itself). Both divergences are pinned by tests. - The docs no longer claim the dbt deploy log covers a native producer that drops its `// materialize`; it does not, and nothing else reports that case. - An integration test over the deploy contract, since only a real deploy proves the handler feeds `sole_dbt_producer` the canonical key `asset.path` holds — the spelling that has to agree across the materialize target, the `// on` ref and the refusal that joins them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: qualify the any-language claim, and pin the dbt-script refusal `AssetKind::Dbt`'s contract (both enums), the two runtime guides and the deploy comment said a script of any language may declare a `dbt://` write, which the dbt-script refusal added last round contradicts. They now say "any language but dbt's own", with the reason: a project's writes are read from its manifest. The deploy-contract integration test covers that refusal for both annotations. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: teach the pipeline AI guidance the warehouse-relation target The pipeline prompt (both sources, plus the regenerated bundle) told the model `// materialize` is DuckDB-only and rejected on any other target, which now steers users away from the very thing this PR adds. It distinguishes the managed DuckLake write, still DuckDB-only, from the warehouse-relation declaration any language but dbt's own may make. `dbt_manifest.rs`'s module doc carried the same "the only thing that creates one" overclaim the other four sites lost last commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: draw an explicit dbt:// subscription on the canvas The editor suppressed every `// on dbt://…` overlay, which was right while the deploy refused all of them. It now refuses only a relation dbt alone builds, so the suppression hid the author's own annotation for exactly the case this PR adds — a subscription woken by a native `// materialize manual dbt://…` producer. The deploy stays the gate. Also the two stale claims round 4 named: the live pipeline prompt dropped the dbt-script exception the base prompt carries, and the doc's e2e requirements still said every `dbt://` subscription is refused. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: refuse `// data_test` beside a `dbt://` materialize target `// data_test` checks are verifier probes the DuckDB executor splices around a managed write. A warehouse relation is written by the script itself, in any language, so nothing would run them — and unlike the DuckLake `manual` case, which at least fails loudly in that executor, a declarer in another language deployed green with its data-quality assertions silently skipped. Covered in the deploy-contract test and documented beside the annotation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: exclude a renamed producer from the sole-dbt producer set The producer set already excluded the deploying script's own path, because its committed rows describe the version being replaced. Under a rename the write sits at the OLD path — still committed, and removed by the same uncommitted transaction — so a producer renamed while it drops its `// materialize` and adds `// on dbt://…` still counted as the producer that would wake it, and committed a dormant edge. The deploy-contract test covers it: without the exclusion the rename deploys 201 instead of being refused. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: take the rename test's parent hash from the create response `format!("{:x}", …)` over the stored i64 drops leading zeros, while `ScriptHash`'s deserializer hex-decodes and demands 8 bytes — so a hash below 2^60 would 422 the request instead of reaching the refusal it asserts on, on roughly one in sixteen spellings of that script body. The create response already carries the zero-padded form, as the rest of the suite uses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: state the concurrent-ingest interleaving honestly `sole_dbt_producer`'s doc claimed the concurrent-deploy race only ever resolves toward refusing. It does when the uncommitted producer is native; when it is the dbt ingest, the check sees an empty producer set and accepts, and if that ingest then commits and runs its warning query before the subscriber's trigger row lands, neither side reports the dormant edge. Not serialized: the two would have to share a per-relation lock, and the ingest takes `script … FOR UPDATE` before its own advisory lock, so a deploy holding relation locks first inverts that order into a cross-subsystem deadlock — a worse failure than the cosmetic edge. Recorded beside the other orphaning the deploy cannot catch, with the bound both share: the next deploy of that project warns. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: refuse a `dbt://` subscription that is not a whole relation `# on dbt://main/analytics` deployed and persisted a trigger row. Every producer spells `<warehouse>/<schema>/<name>` — the manifest ingest derives it from `relation_name`, a `// materialize` target is checked against it — so a partial one is an edge nothing can ever wake, which is what the dbt-only refusal exists to prevent. The shape now has one definition (`is_full_relation_path`) that both halves of the deploy ask, rather than a segment count spelled twice: a subscription and a write that disagreed would refuse and accept the same string. Also rewrites the canvas test's comment as a current constraint per AGENTS.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: hold both halves of the deploy to one `dbt://` relation validator A subscription checked the relation's shape but not its warehouse, so `# on dbt://<unconfigured>/<schema>/<name>` deployed and persisted a trigger row for something no producer can ever write: the write side refuses that exact string, and a dbt project's `profile.warehouse` resolves against the same config, so no later deploy fixes it and the dormant-edge warning cannot report it either. The shape rule and the warehouse rule now live in one `validate_dbt_relation` that both halves call, rather than being spelled per site — the previous two rounds each closed one half of one rule, which is the drift that invites. Also moves the parser test out from between a comment and the test it documents, and names both refusals in the doc's list. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: drop the subscription-only clause from the shared refusal message "so nothing can produce it" reads backwards on the `// materialize` side, which is the producer. The remaining sentence says what is wrong on both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: bound a `dbt://` relation by the asset-path column in the shared validator `asset.path` is VARCHAR(255) and the manifest ingest drops a relation that outgrows it rather than failing the whole graph, so past the column no producer row can exist on either side. `script_trigger.trigger_ref` is unbounded text, so an overlong subscription deployed and stayed dormant for good; an overlong write reached Postgres and failed the deploy on a `value too long` instead of a message. Both now refuse in the validator the two halves share, against the ingest's own constant. The integration case computes the ref from that constant so it cannot drift back under the bound. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: report a warehouse-lookup failure as the failure it is, and correct the boundary `dbt_warehouse_exists` fails three ways — no such warehouse, the query itself, and a setting with no `resource_path` — and all three became a 400 blaming the user's warehouse name. A pool timeout mid-deploy told a retrying sync that a transient server error was a permanent client one. Only `NotFound` is the annotation's fault now. The known-boundary paragraph claimed a flow-runner run still cascades. It does not: it is routed by `flow_step_id`, which `is_eligible_kind` rejects, as `asset_trigger_dispatch.rs` pins. Recording and cascading are decided separately, so the paragraph now names all three routes rather than merging two of them — and the row it omitted, an ordinary flow step, which records and never cascades. E2E item 7 said "deployable" where the rule is "wakeable": with only the dbt project reading the relation the producer set is empty, which deploys fine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: correct two rationales the last commit got wrong `Error::SqlErr` already maps to 400 in this codebase, so the query case's status was never the thing at stake. What the `NotFound` match earns is that a query failure and a malformed setting stop being described as an unconfigured warehouse name, and that the malformed-setting `InternalErr` reaches its own 500 instead of being flattened. And a flow step is two shapes, not one: a step running a deployed script is a `Script` job that records and never cascades, while a step with an inline body is `FlowScript`, which the recording guard excludes along with previews. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: warn about dormant subscriptions from the run that publishes ownership too A run whose static descriptor finds its profile moved re-ingests the version's graph and republishes path ownership, exactly as a deploy does — so it can be what leaves a subscription accepted while the relation had no producer with dbt as its only one. That path discarded `persist_ingest`'s result and emitted no warning, which also made the doc's enumeration of unreported orphanings wrong. Both ownership-publishing points warn now. An agent worker still cannot: it reaches these tables only through the API and its ingest publishes without reading back, which the doc now says. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: an agent run publishes no ownership, and the warning has two callers The agent-worker sentence called it an exception that publishes ownership without warning. It publishes none: `Connection::Http` forces per-run models, and `publishes_ownership()` is the negation of that, so an agent stores a job-pinned snapshot and leaves workspace ownership with the deployed graph — it cannot orphan a subscription at all. `warn_dormant_subscribers`' own doc still named the deploy log as the only place the warning shows, one commit after it gained its second caller. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: stop the managed-write rule from contradicting the dbt:// target The sentence after the warehouse-relation paragraph says `// materialize` means the runtime writes the table for you and the body is a bare SELECT. That is the managed DuckLake rule, written before a `dbt://` target existed, and unqualified it tells the model the opposite of what the paragraph above it just said — a model following the more prominent one emits a SELECT for a warehouse relation, which deploys and then writes nothing. Both prompt sources now scope it, and both name the `// data_test` refusal beside a `dbt://` target, which the badge list advertised without the caveat. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5da4ea43fb |
feat: show the new-tab icon on a chat path pill while the modifier is held (#10976)
* feat: show the new-tab icon on a chat path pill while the modifier is held Fixes WIN-2477 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq * fix: read the new-tab modifier in the capture phase Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq * refactor: track the new-tab modifier only while a pill is hovered The window key listeners were installed at import time and never removed, so every page that loaded the module paid for them whether or not a pill existed. They now attach on mouseenter and detach on mouseleave or destroy, which is the only window in which the answer is read. Seeding the flag from the hover event also removes the limitation the previous version documented: a mouse event carries the same modifier flags as a key event, so a modifier held before the pointer arrived, or while this window was unfocused, now reads correctly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq * refactor: export the new-tab modifier as a read-only view `newTabModifier` handed every consumer a writable handle on module-global state, so any of them could drive the icon of every pill on the page. The getter form is what frontend/AGENTS.md prescribes for shared reactive state. Tearing each attachment down in the test's afterEach as well: the module state and its window listeners outlive the DOM, so emptying the body left `held` and the hovered node set for the following case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq * refactor: only track the modifier for pills whose icon can change The attachment went on every path pill, so hovering a drawer or plain-link pill installed three window listeners for a flag its icon never reads. Only a preview pill can flip, so only it gets them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq * fix: re-read the new-tab modifier from pointer movement A modifier held across a keyboard app switch was cleared by the blur and never restored: the key was down the whole time so no keydown arrived on the way back, and the pointer parked on the pill fired no fresh mouseenter either. The pill then showed the panel icon while the click would have opened a tab. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq * refactor: give each pill its own modifier state The shared module state forced a node-identity guard: one hovered element owned the window listeners, so a pill destroyed elsewhere in the transcript had to be stopped from tearing them down. A factory per pill removes the guard, its test case, and the whole class of cross-instance interference, and narrows re-renders to the hovered pill instead of every preview pill on screen. Listener teardown now goes through AbortController signals, so leaving a pill drops the whole set at once rather than through a remove list that has to mirror every option exactly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq * fix: abort the previous hover controller on re-entry A second mouseenter with no mouseleave between replaced the controller without aborting it, so the four listeners registered under the first signal outlived even the element's destruction: neither leave nor the destroy path held a reference to reach them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f037c73d10 |
feat(frontend): group the agent form and edit saved agents as drafts (#10880)
* feat(frontend): group the AI agent step form and edit saved agents in a modal Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: edit a saved AI agent through its own resource draft Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: drop the agent fork-for-edit session now that edits live in a draft Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: edit ai_agent resources from the resources page with the agent editor Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: send a standalone agent's brain from the module when testing a step Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep the agent draft faithful to the resource it deploys to Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: add the sqlx cache entry for the eval subject rename Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: share the module insert between the graph and the agent editor Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(frontend): open evals inside the agent editor, actions in its header Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(frontend): add tools from the agent editor and lighten its test pane Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): open an ai_agent deep link in the agent editor Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): drop the failed result badge on a step that never ran Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(frontend): head the agent editor's levels with a back control Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(frontend): drop connect and fill inputs from the agent editor Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(frontend): lighten the agent editor's run panel Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): stop a nested agent tool's config reading as AI-filled Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(frontend): offer only AI or static on an agent tool's inputs Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(frontend): give a saved agent's tool editor a static-only surface Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(frontend): open an agent tool in a drawer beside the agent Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(frontend): hide unset agent config in the run form Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(frontend): share the input forms' pickers and s3 lookup Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(frontend): drop a dead agent-editor export and fix two stale comments Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): reach an ai_agent's resource-level settings and copilot Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): open an ai_agent's resource view as JSON, not the generic form Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): address review findings on the agent editor's draft and streaming Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): close the agent editor on a version restore, as the resource editor does Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): stop the provider picker auto-writing a kind, and clear review nits Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(frontend): drop the fork-for-edit leftovers from the agent card Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): mount the agent editor in the dev flow editor and guard the deep-link race Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): deploy the agent config that was submitted, and refuse one no run could use Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(frontend): build the agent editor's rows from the design-system button Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): keep a draft-only agent's draft, and let a blank MCP summary deploy Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): guard read-only agents, incomplete MCP tools and duplicate editor mounts Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: read-only agent editor, linked-card refresh, atomic eval rename * fix: eval rename needs the privileged pool, per-workspace write access * fix(frontend): drop the agent editor target when its mount goes away * refactor: drop the agent rename work from this PR, unban the bindable defaults * fix(frontend): refuse a renaming deploy and drop the copilot from static-only fields * fix(frontend): mirror the worker's streaming rule and scope agent writes to their target * fix(frontend): read runtime streaming as off and reset the drawer's json view * fix(frontend): read an unsettled output_type as non-streaming too * fix(frontend): let the showing modal claim an agent opened from inside it * fix(frontend): keep in-flight edits, tool replacements and every linked step in sync * fix(frontend): keep attachments in the run form and bind the agent ref to its tools * fix(frontend): preview the agent as authored and re-evaluate step args on run * fix(frontend): scope agent-editor ownership to the flow's workspace * fix(frontend): drop the tool drill-in where there is no graph to select on * fix(frontend): require a provider kind and keep one resource editor open at a time * fix(frontend): keep legacy nulls, static-only text literal, and the handover anchor * test(worker): pin the agent streaming default * fix(frontend): let an AI-fillable input be switched to static * fix(frontend): report agent editor background failures instead of floating them * fix(frontend): keep the version pane's path alive while the editor closes * fix(frontend): clear the anchor-keep flag at the start of each drawer session * fix(frontend): preview the agent without its synthetic path, refresh the baseline on external writes * refactor(frontend): drop the unverifiable baseline refresh, state the synthetic-path rule * fix(frontend): keep the synthetic path out of agent tool test runs too * refactor(frontend): host the agent editor under the agent's own path * fix(frontend): mark an agent editor's host explicitly instead of inferring it from the path * fix(frontend): discard linked-agent responses from before a deploy * fix(frontend): keep a flow mount from claiming an agent editor's nested target * feat(frontend): keep an agent used as a tool inside the agent being edited * fix(frontend): reserve the agent editor's root module id * docs(frontend): record why the agent editor previews under the agent's path * fix(frontend): refuse to open or deploy a resource that is not an agent * docs(frontend): put the scope-migration comment on the function it describes * fix(frontend): refuse an agent path whose resource type is not proven * fix(frontend): recheck the resource type before deploying, and keep expressions off static-only inputs * fix(frontend): lazy-load the agent editor and slide its levels like the evals pane * refactor: drop unreachable non-list tools check from agent deploy Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): clear text-only agent fields on image output, reserve the root id Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): keep the agent editor usable for a non-list tools value Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): stop the parked eval run list from taking arrow keys Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): report a non-list tools value on deploy instead of throwing Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): keep temperature editable for image output Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): skip non-object tool entries when rendering an agent Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): guard tool entry reads instead of copying the tool array Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): key tool rows by position so duplicate ids render Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fda7b3f086 |
feat(ai-sessions): replace the context panel with an assistant settings modal (#10919)
* 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 * feat(ai-sessions): add a context panel listing what the chat can use Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmdCVM1ZvTcpatv78jN8Ed * fix: track the prompt rebuild signal and trim the review round's nits Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmdCVM1ZvTcpatv78jN8Ed * fix: keep the panel from perturbing an in-flight turn Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmdCVM1ZvTcpatv78jN8Ed * fix: count a folder by its readable children Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmdCVM1ZvTcpatv78jN8Ed * feat(ai-sessions): replace the context panel with an assistant settings modal Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * feat(ai-sessions): page-based MCP editing and fuzzy tool search Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * feat(ai-sessions): tool detail page and a shared list row Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * feat(ai-sessions): add a files & folders section to the assistant settings Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * chore: point the ee ref at the merged ee branch Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * fix: keep hidden sections from answering keys and swallowing a failed save Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * fix: restore the staged-fork write guard and narrow the round-2 findings Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * fix: restore the workspace-race guards and extend them to MCP Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * fix: keep an in-flight settings read from overwriting typed instructions Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * docs: describe the tool row as the one line it renders Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * fix: keep the prompt entries on the home composer, which has no settings modal Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * fix: create the editor with the gutter its caller asked for Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * test: restore the attachment status label guard dropped in the merge Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * fix: surface a refused mcp selection write instead of painting the switch Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * fix: refuse instruction writes to a staged fork's parent, and read the target's role Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * fix: pin the instructions role and field to the target workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ld1m9NAGLdPPrNBiu5PSQK * fix: retry a deferred instructions reload, and use Button for the row label Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGnKFvaX61wiMU1CzQp6XG * fix: leave the arrows to a control that answered them, and say when a role read failed Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGnKFvaX61wiMU1CzQp6XG * chore: update ee-repo-ref to a2776856c50e80c9dbcf6e689a66ce86567c03fa This commit updates the EE repository reference after PR #765 was merged in windmill-ee-private. Previous ee-repo-ref: dd7466e749753568a23c91ba5e165020769206b8 New ee-repo-ref: a2776856c50e80c9dbcf6e689a66ce86567c03fa Automated by sync-ee-ref workflow. * fix: withhold the page navigator from a parked section Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGnKFvaX61wiMU1CzQp6XG --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Guilhem Lemouel <guilhemlemouel@gmail.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
e39dd7eb12 |
docs: teach agents to pass a resource as $res:<path> in run arguments (#10927)
* 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> |
||
|
|
f10ac6c2b3 |
feat: open path links from chat messages in the session preview panel (#10881)
A workspace path mentioned in a chat message rendered as a link that always opened a new browser tab. On the sessions page, which hosts a preview panel, a plain click now opens the item in that panel instead. Modifier clicks still reach a new tab, and surfaces with no panel keep their previous behaviour. Scripts, flows and raw apps are supported. Legacy drag-and-drop apps are not: the panel has no editor that can host one, so their links stay outbound. The link pill's kind icon and action icon now cross-fade inside a fixed 12px box, so the pill is the same width at rest and on hover and the surrounding sentence never reflows. `openItemPreviewAction` moves to a new import-free leaf module so a chat message can reach it at runtime without dragging monaco, zod and the openai client into the render path. Claude-Session: https://claude.ai/code/session_01RjbVL7h9NiTLGTgyfiHvXG Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
fdd3b36423 |
feat: workspace setting to hide the AI assistant, agent steps unaffected (#10941)
* feat: workspace setting to hide the AI assistant, agent steps unaffected Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eNweUugVqerex6MLxjbeL * fix: load workspace AI config on cold /sessions load and say hidden, not disabled Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eNweUugVqerex6MLxjbeL * fix: follow workspace switches on /sessions gate and drop deprecated button size Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eNweUugVqerex6MLxjbeL * fix: key the /sessions hidden-assistant gate on the acting workspace's own config Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eNweUugVqerex6MLxjbeL * fix: tag the /sessions hidden-assistant verdict with its workspace and drop superseded reads Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eNweUugVqerex6MLxjbeL * fix: overlay the /sessions hidden-assistant gate so warm sessions survive workspace switches Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eNweUugVqerex6MLxjbeL * fix: hide the pipeline insert menu AI prompt and refuse chat turns where the assistant is hidden Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eNweUugVqerex6MLxjbeL * fix: shrink the home Build with AI / CLI / Hub line to a flush hint row Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eNweUugVqerex6MLxjbeL * fix: frame the workspace toggle as hide AI sessions at the bottom of the AI settings Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eNweUugVqerex6MLxjbeL --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
9074de25ea |
fix: resolve chat path links against the session's operating workspace (#10924)
* fix: resolve chat path links against the session's operating workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RGq2deVkz8qnpfssssKzn7 * fix: hide the chat link drawer button where nothing can open it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RGq2deVkz8qnpfssssKzn7 * fix: hide the chat tool card open button where nothing can open it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RGq2deVkz8qnpfssssKzn7 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
816dc9dcd2 |
feat(ai-sessions): show a running session across tabs and reload finished turns (#10916)
* fix(ai-chat): make a disabled composer look disabled Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(ai-sessions): show a running session across tabs and reload finished turns Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): keep queued drafts through catch-up and hold locks by identity Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): carry pastes through refusals, spare resends and auto-resume Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): retry held auto-resume, keep the footer, spare bfcache freezes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): give each driving tab its own lock slot Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): release refused synthetic sends and use a text key separator Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): merge late-refusal restores and keep attachment-only edits Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): patch the stored chat pointer instead of rewriting the record Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * docs(ai-sessions): align the run-signal comments with the code Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): retry transient catch-up skips and gate the remaining send paths Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * docs(ai-sessions): name the chat-id seeding path persistTouched defers to Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
cfcfe298dd |
feat(ai-chat): make reusable skills ai_skill resources you select per workspace (#10914)
* 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> |
||
|
|
c512110a1f |
fix(ai-chat): consume an @ mention with the message that carried it (#10907)
An `@`-mentioned workspace item stayed in `selectedContext` after the message that mentioned it was sent, so every later turn in the session restamped it into `## SELECTED CONTEXT`. Treat those mentions the way a DOM pick is treated: attached to the one message that carried them. The composer pins the live selection as `contextOverride` at the click and clears the mentions in the same synchronous gesture, so the send keeps what the user picked for it and the next draft starts clean. When a send hands its text back to the composer, the mentions it carried come back with it. Scoped to GLOBAL. In SCRIPT/FLOW/APP the mentions still stay selected as chips the user removes by hand, so `isMentionContext` is membership only and every caller gates on mode. Claude-Session: https://claude.ai/code/session_01HxGz1YsvW5Kwmn8THAUrwB Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
716ce2ece0 |
feat: free AI tokens + home search/filter revamp (#10020)
* feat: add free Claude Opus tier with per-user token limit Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * nit move alert * Home AI Chat * wire home ai chat * auto send prompt * refactor: remove keyboard arrow-navigation from home list Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: replace home search bar with unified FilterSearchbar Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: replace home quick tags with FilterSearchbar presets Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: add content filter to home FilterSearchbar with EE-gated content view - Clear the kind filter by deleting the key (was showing a 'kind: null' tag on All) - Remove the standalone Content button - Add a 'content' filter; when set, render the Ctrl-K content-search view (ContentSearchInner) which shows text-match snippets and its own EE warning Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: disable home AI chat and prompt to configure AI when no model Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * track cost instead of tokens * nit * fix: load copilot config on home so AI chat isn't wrongly gated Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Home page update * nits * example prompts * nit * feat: switch free AI tier to DeepSeek with daily cost budgets Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * nit * Move bottom buttons to HomeAIChat * [ee] feat: surface free AI tier state and make its metering abort-proof Makes the free Windmill AI tier legible to the user and closes an abuse hole. Backend: - AIConfig gains a response-only free_tier marker (skip_deserializing so a client can't store a forged one via edit_copilot_config). get_copilot_info keeps returning it once the grant is spent, so the client knows AI is off because the grant ran out, not because nothing was configured. - Per-user grant becomes one-time (migration drops the day key from ai_free_token_usage); the daily table stays as the instance kill-switch. - Reserve-then-reconcile metering (see EE commit) so a mid-stream disconnect can no longer dodge the usage report and get metered zero. Frontend: - copilotInfo carries freeTier; model settings show a "Free" pill and a usage meter that warns past 80%. - The home chat and the session chat show a dedicated "you've used your free Windmill AI, add your own API key" state instead of the generic "no provider configured" one. - A failed send re-fetches copilot_info so the exhausted state (and its banner) appears live, without a page reload. Bumps ee-repo-ref.txt to the matching EE commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: free AI usage meter reusing the context-usage gauge Show free-tier spend with the same gauge as context usage instead of a bespoke block: - Extract the meter+tooltip into a shared UsageMeter; ContextUsageIndicator uses it, and a new FreeTierUsageIndicator renders it from copilotInfo.freeTier. Placed in the session-chat toolbar and next to the home-chat model settings; the old meter block in the model-settings dropdown is removed (the "Free" pill stays). - Hide the context-usage bar while on the free tier so the free meter takes that slot. - Refresh copilotInfo after every free-tier turn (AIChatManager finally) so the meter advances live and the turn that exhausts the grant flips to the exhausted state, instead of both only updating on reload. Gated to active free-tier users, so it costs nothing for configured-key users. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: fix stale free-tier comments after DeepSeek/cost rework Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: always show context bar, replace free-tier meter with usage banner Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * nit * fix: atomic free-tier budget reservation (ee ref + sqlx) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep CLI/MCP and Hub buttons unblurred on AI chat hover Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add back arrow nav * nit * nit * fix: three review P1s in the home AI chat & search - AIChatManager: refreshFreeTierUsage now bails unless the global copilot state still belongs to the completing manager's workspace, so a warm session finishing after a workspace switch can't reload its (background) workspace over the active one's models/client/copilotWorkspace. - HomeAIChat: block submission until the copilot config is loaded AND enabled (new `canSend`), so a prompt submitted during the unknown-config window isn't handed to a session that never sends it and silently lost. The disabled overlay still gates on config-loaded to avoid a flash. - ItemsList: the content-search reload effect now depends on $workspaceStore so content results follow the active workspace instead of showing the previous one's. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [ee] fix: harden the three home-AI-chat/search P1s after deeper review Follow-up to the previous P1 commit; sharper review found the earlier guards insufficient: - refreshFreeTierUsage now compares against the most-recently-*requested* workspace (new copilotWorkspaceRequested in aiStore, set synchronously in loadCopilot), not the last-*resolved* one — otherwise a warm session finishing while a newer workspace's load is still in flight could win the monotonic token and restore its stale workspace over the one being loaded. - The content-search view is keyed by workspace ({#key $workspaceStore}) so a switch remounts ContentSearchInner; late in-flight responses from the previous workspace can no longer land in the new one's component. Backend (EE, via ee-repo-ref bump to 03ef0eb): the free-tier reservation now also prices the worst-case input cap (at the cache-miss rate), and enforce_free_tier_body rejects oversized prompts and pins n=1 — so an aborted large-prompt request can no longer dodge the input bill that reconciliation would otherwise charge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: exclude service accounts from the free AI tier Free-tier eligibility was keyed solely on authed.email. Workspace admins can create and impersonate arbitrary service accounts (synthetic *.sa.wm.dev identities), each of which would receive its own one-time grant — letting one tenant mint many grants and drain the instance-wide daily allowance. Skip the free-tier fallback for *.sa.wm.dev identities. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: activate free AI tier when clearing a workspace provider edit_copilot_config returned AIConfig::default() when the saved workspace config had no providers and no instance config existed; the frontend applies that response immediately, disabling AI even though the free-tier key is available. A later get_copilot_info (on reload) returns the synthetic free-tier config, so clearing a provider behaved inconsistently until reload. Give this response path the same free-tier fallback as get_copilot_info. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: gate the home AI composer behind the global-AI dev flag The "Build with AI" composer starts a session and navigates to /sessions, which lives behind the same wm_dev_global_ai dev gate as the global AI chat. With the gate off (the default), /sessions renders only its gate message, SessionWrapper never mounts, and the queued prompt is silently dropped. Hide the home entry point behind isGlobalAiEnabled() so it isn't exposed before the sessions gate opens. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [ee] chore: bump ee-repo-ref for deepseek-v4-flash price/model fix Points at the EE commit that pins deepseek-v4-flash and its real prices (pico-precision accounting). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [ee] fix: provable byte bound for the free-tier input cap (ee-repo-ref) Bumps ee-repo-ref to the EE commit that caps the raw request body byte length directly (token_count <= byte_count is provable), replacing the unsafe body.len()/2 token estimate that high-entropy prompts could beat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * nit isGlobalAiEnabled * empty commit * fix(frontend): address Codex review on free-tier / home filters - P1: home filters now sync from the URL reactively, so browser Back/Forward updates the chips, kind toggle and results (and clears keys dropped from the URL) instead of leaving them stale until the next filter edit. - Free-tier banner buttons drop deprecated Button props (size/color/border variant) for unifiedSize + a supported variant. - Condense refreshFreeTierUsage comments to a single race-condition constraint beside the guard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): hide empty kind badge on draft-only scripts A draft-only script can carry an empty `kind`, which still isn't 'script' so the row rendered a blue badge whose only content was capitalize('') — an empty pill left of the "Draft only" badge. Guard the badge on a non-empty kind. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(frontend): animate home tree-view group expand/collapse Wrap each owner group's children in ResizeTransitionWrapper so height changes animate. A slide transition only animates the initial mount, but a freshly-opened owner fetches its rows and passes through a transient empty state before they land — the ResizeObserver animates that second growth too. Nested TreeViews inherit the wrapper's context and skip their own, so one observer per top-level owner animates the whole subtree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(frontend): FilterSearchbar boolean auto-set and string-filter presets - A default-false boolean filter has only one useful value, so selecting it sets true immediately instead of opening a true/false picker. A default-true boolean (e.g. "Include library scripts") still shows the picker, where false is the meaningful choice — expressed via a new optional `default` on the schema. - A plain string filter now surfaces any presets targeting it (`<tag>:<value>`) as suggestions once selected, integrated into menuItems so keyboard nav works — previously selecting e.g. "Owner" showed nothing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(frontend): home page toolbar and content-filter revamp - "New" create-menu button (scripts/flows/apps/…) replaces the old Content button; the search bar moves to the right of the toggle group. - Restore the content filter dropped in a merge: a `content` searchbar filter swaps the list for the full-text ContentSearchInner view (EE), aligned flush with -mx-2. - Move the owner/group and label chips off the page into FilterSearchbar presets; ownerFilter/labelFilter now derive from the searchbar keys (data layer unchanged). - Move the list controls (select / tree view / expand-all / sort) inline into the top row between the toggle group and search bar; add margin above the list. - Beta tag on the home AI chat; a bit more bottom margin under it; tighten the gap between the admin/tutorial banners and the list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai): pass the request body to the free-tier reservation Thread the prompt body into resolve_free_tier_credentials so the free tier can size its upfront reservation from the actual request length instead of a fixed worst case (EE c2e248b), fixing normal chats being rejected as "too large". Updates the OSS stub signature and bumps ee-repo-ref. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): gate home Create/Import menu on edit permissions The relocated CreateActionsMenu rendered unconditionally, so operators and users in workspaces protected from direct deployment saw create/import actions they can't use. Restore the original gate (!operator && showEditButtons, the latter from NoDirectDeployAlert). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): address Codex review on filter searchbar - P1: the boolean shortcut now goes through the same tag-insertion path as the normal branch, so it removes the typed search segment instead of leaving it as a stray free-text (_default_) term. - Mark the Runs `show_future_jobs` filter default: true so selecting it opens the picker (false is the meaningful choice) rather than being a no-op. - Home owner/label presets now emit the canonical `key:\ value` form so the applied-preset check matches after a reparse and can't re-offer a duplicate; update the suggestion extraction to strip the leading separator. - Replace deprecated Button props (size/spacingSize/color) on the relocated list controls with unifiedSize. - Fix stale comments: UsageMeter no longer claims a free-tier consumer; the home filter schema comment describes presets, not the removed ListFilters/label badges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): boolean filter shortcut sets value canonically The round-1 shortcut baked `true` into the tag text, which merged into a following tag (e.g. `archived:\ truekind:\ flow`). Instead remove the typed segment, set the value, and reparse so the text is rebuilt canonically — no lingering free-text and no merge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(ai): restate free-tier caller identity contract in the OSS stub; bump ee-repo-ref Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): keep flanking tags separate when boolean shortcut drops a segment Joining `before`/`after` directly fused the tags a removed mid-segment sat between (e.g. `kind:\ flowsummary:\ bar`). Join with a space; reparse then canonicalizes. Also trims the comment to the essential constraint. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(ai): update sqlx cache for free-tier daily-day queries; bump ee-repo-ref The reserve/reconcile daily-usage queries now bind the reservation day (EE change); refresh their offline query cache and point ee-repo-ref at the EE commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai): activate free tier when instance ai_config has no provider An instance ai_config row won precedence just by existing, so an empty {} (valid via global settings / declarative config) suppressed the free-tier fallback and left AI disabled — even though build_copilot_settings_state already treats it as unconfigured. Apply the same has_providers() check to the instance config in the proxy and edit_copilot_config paths. Also refresh the sqlx cache for the reservation ceiling change and bump ee-repo-ref. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): migrate legacy Home filter URLs to the searchbar keys The old Home UI stored free-text in `search`, owner scope in `filter`, and could write `kind=all`; the generic searchbar sync uses `_default_`, `owner`, and a kind enum without `all`. Rewrite those params once before the sync reads the URL so shared/bookmarked links restore, and drop `kind=all` which would otherwise wedge later filter edits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai): empty instance config in get_copilot_info; label user-disabled Home AI - get_copilot_info returned any existing instance ai_config row before the free-tier fallback, so an empty {} disabled AI in the copilot-info UI even though the proxy now serves the free tier. Apply the same has_providers() gate here. - The Home chat overlay said "No AI provider is configured" when the user had disabled AI in account settings (providers still present). Distinguish that state ("Windmill AI is disabled in your account settings") as the docked chat does, and drop the misleading workspace-config button in that case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(ai): drop redundant proxy service-account check; trim TreeView comment The service-account exclusion now lives in the free-tier helper, so the proxy calls it directly. Also condense the tree-view resize-transition comment to the essential reason. Bumps ee-repo-ref. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(frontend): the Home content filter is not EE-gated ContentSearchInner loads the workspace's scripts/flows/apps/resources and matches their contents client-side, so it works on any instance. Drop the misleading "(EE)" from the filter label and the "EE indexer / off-EE fallback" comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(ee): bump ee-repo-ref for free-tier pricing + exhaustion fixes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): show disabled Home AI overlay statically, not on hover The disabled-state overlay (reason + configure/add-key action) was opacity-0 and pointer-events-none until group-hover, so keyboard and touch users saw an inert composer with no visible remedy. Render it and the composer blur statically when disabled instead. Also bumps ee-repo-ref for the trimmed free-tier comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): give account-disabled Home AI overlay a recovery action The account-disabled branch showed a reason but hid every action, on the mistaken premise that account settings has no linkable route. It opens from the #user-settings hash (the same one the sidebar Account menu uses), so link there. Bumps ee-repo-ref for the free-tier fixes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): gate Home AI composer for operators; a11y and filter-sync fixes - Home composer now uses prefersSessionHandoff($userStore?.operator) instead of isGlobalAiEnabled(): operators reached this route and could submit a prompt into a /sessions page that refuses them, silently dropping it. Also drops the leftover empty header spacer div above the chat. - HomeAIChat: mark the blurred/disabled subtrees inert so keyboard users can't tab into the unreadable textarea (pointer-events-none didn't stop Tab). - ItemsList: keep the role-dependent searchbar keys (include_library, only_user_folders) in the schema unconditionally and toggle `hidden` instead, so useUrlSyncedFilterInstance (which snapshots the key set once) still URL-syncs a key that first appears after a workspace switch. - Bumps ee-repo-ref for the indexer non-parquet build fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): keep CLI/MCP connect row for operators; trim filter comment The previous commit gated all of HomeAIChat behind the operator/session check, which also removed the AI-independent CLI/MCP "Connect workspace" drawer that operators (and the sessions-beta opt-out) had on main. Render HomeAIChat for the same audience as before (isGlobalAiEnabled) and gate only the composer (title, input, examples, overlay) on operator status inside the component; the connect row always shows. Also trims the role-dependent filter-schema comment to the <=4 line rule. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): reconnect Home keyboard navigation to the unified searchbar The searchbar migration replaced the <input id="home-search-input"> the ItemsList keyboard handler keys off, so Arrow/Enter no longer drove the results list. Thread an `id` down to the searchbar's contenteditable (via TaggedTextInput/FilterSearchbar `inputId`) so the handler and the workspace-switch focus restoration find it again; read the caret through the Selection API instead of an <input>'s selectionStart/End; and stand the list's arrows down while the searchbar's suggestion dropdown is open (tracked via onDropdownVisibleChange). In free-text mode the searchbar no longer opens its dropdown on a bare arrow key, so an empty box passes Arrow/Enter to the list as before. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): stop searchbar Enter inserting a newline; idle typewriter for operators - TaggedTextInput is a single-line filter input, so Enter now preventDefaults the contenteditable's newline insertion (surrounding suggestion-select / list-open handlers still run on bubble). Previously Enter with no row highlighted dropped a literal \n into the query. - HomeAIChat's placeholder typewriter effect now runs only while the composer is shown, so it no longer loops forever driving an unrendered input for operators. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * chore: update ee-repo-ref to f2a31156ac08ecb02d89dbc66d72be58e9c877ff This commit updates the EE repository reference after PR #652 was merged in windmill-ee-private. Previous ee-repo-ref: e59b96a2eea5d1110b40c842f17b337ab051bdd3 New ee-repo-ref: f2a31156ac08ecb02d89dbc66d72be58e9c877ff Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
d334831735 |
fix: reject a prefixed error_handler_path on triggers (#10847)
* 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> |
||
|
|
25a3e6ea7a |
fix(ai-chat): keep the composer usable while a question is pending (#10816)
* fix(ai-chat): keep the composer usable while a question is pending Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL * fix(ai-chat): keep a typed answer when the question's resolver is gone Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL * fix(ai-chat): only advertise the answer affordance on a live question Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL * style: trim the pending-question rationale comments to the 4-line cap Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
541b6c8496 |
fix: keep ai chat messages when leaving the page mid-generation (#10809)
* fix: persist ai chat turns mid-generation so leaving the page keeps them * fix: stop chat checkpoints once the turn commits, keep streamed text visible * fix: checkpoint streamed answers as they grow and keep half-run tool batches * fix: checkpoint text as received so a backgrounded tab keeps capturing * fix: keep buffered tool screenshots in mid-batch chat checkpoints * fix: decide committed-text at the flush site, condense checkpoint comments * fix: checkpoint only live streamed text, never text the parser owns * fix: don't swap the chat transcript out from under a running turn * fix: close the pre-loading window in the conversation-switch guard |
||
|
|
b6e059116a |
feat: track token cost in AI sessions and chats (#10688)
* feat: track token cost in AI sessions and chats * fix: address review findings on AI cost tracking * fix: price inherited and overridden models at their real rates * fix: stop newer model revisions inheriting an older price * fix: stop a sub-model inheriting its family's price * fix: keep alias suffixes resolving to their model's price * fix: count OpenRouter cache writes and drop unverifiable rates * refactor: move AI spend out of the chat into workspace and user settings * fix: pin the usage workspace per turn and stop inventing cache rates * fix: leave Sonnet 5 unpriced while its promotional rate runs * docs: record the new table in the schema summary and tighten comments * fix: mark estimated AI costs with ~ and drop session grouping Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: name the workspace in the self-scoped AI usage title Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: state that overrides never replace a provider-returned cost Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: let a cleared cache rate inherit again and flag partial totals Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: clear a refused rate's error when the input snaps back Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop a revision variant inheriting its base family's rate Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: report AI usage before tools run and price self usage consistently Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: key pricing rows on the model id usage is reported under Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: surface Bedrock and Gemini usage the chat proxy was dropping Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: count Gemini tool-use prompt tokens as input Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: price flat-rate Gemini Flash models Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: state the tool-use token invariant once Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0b3dc3e5c9 |
fix: build the global chat's prompt identity from the operating workspace (#10793)
* fix: do not read an unloaded workspace list as a non-membership `roleForWorkspace` settled `not_a_member` from `userWorkspaces` alone. That store and `superadmin` both start undefined and load asynchronously, so an unloaded list read as an empty one: a chat operating on any workspace other than the one being browsed advertised no pages and reported an access denial. The root layout gives up after its retries, so a load that fails leaves the denial permanent, with `whoami` never attempted. Settle a non-membership only once both stores have resolved; treat unresolved as unknown and fall through to the `whoami` lookup. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: build the global chat's prompt identity from the operating workspace The prompt's path conventions and folder guidance came from the ambient `userStore`, which describes the workspace being browsed rather than the one the chat operates on. Three of those fields are per-workspace and wrong whenever the two differ: the username (that workspace's `usr` row), the writable/readable folder sets (its ACLs), and `is_admin`, which decides whether the folder list reads as exhaustive. The backend still enforces the ACLs, so the cost is prompt quality — paths the model cannot write to, and a 403 to recover from. Resolve the identity for the operating workspace and feed that to the prompt, refreshed alongside skills and MCP servers and settled in `beforeSend` so the cached system-prompt prefix stays stable for the turn. An unresolved role now leaves the folder sets undefined rather than empty, so the guidance is dropped instead of claiming there is nothing to write to, and `create_folder` credits the workspace it wrote to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: read the AI provider resource types lazily `Object.keys(AI_PROVIDERS)` at module scope made `AI_PROVIDERS` a load-time requirement for every importer of this module, the global chat included. `AIChatManager.test.ts` mocks `../lib` without it and has been unable to load since the catalog was introduced; no CI workflow runs vitest, so nothing reported it. The constant is read in two places, both inside functions, so deferring it removes the load-time dependency without changing behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
449b1a6933 |
fix: ground the chat's AI agent provider in the workspace's models (#10774)
* fix: ground the chat's AI agent provider in the workspace's models Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: never reject an AI agent model the catalog could not confirm Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: benchmark AI agent provider grounding in ai_evals Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: only reject an AI agent model an exhaustive listing rules out Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep instance-level AI settings out of the workspace provider catalog Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep untrusted model ids out of the chat's context Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: carry completeness on the model listing itself Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: correct two comments left behind by the catalog rework Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: bound the model listing and verify the default against it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: honour a workspace default a filtered listing names Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: recognise a workspace default past the prompt's model cap Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep an aliasing provider's unlisted model ids permissive Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
8e508ea01a |
feat: support application default credentials for gcp pub/sub triggers (#10778)
* 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> |