mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 16:02:19 +00:00
e052d5ee4598332a6491b653d70fc82a08642d61
8580 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e052d5ee45 |
fix(copilot): only warn on saved write_flow; add regression tests
Address review: writeFlowDraft reports conflicts/persistence errors as
{success:false} rather than throwing, so the empty-body warning must be
folded into the JSON result only on a successful save — otherwise the
model is told to set_flow_module_code on a flow that was never saved
(stale or nonexistent draft). Add core.test.ts coverage for the empty-body
warning (top-level, nested, preprocessor, failure; populated suppressed),
the no-warning-on-failed-save path, and the malformed-JSON escaping hint.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
71f17cea7f |
fix(copilot): stop write_flow forcing rawscript code into nested JSON
The global-chat write_flow tool made the model embed rawscript bodies inside the modules JSON string, so code had to survive three levels of escaping (tool arguments -> modules string -> content string). Models routinely mangled the quotes/newlines and flow creation failed on the first tries. Bring write_flow to parity with flow mode's set_module_code escape hatch: detect rawscript modules left empty or as inline_script placeholders and tell the model to fill them via set_flow_module_code, add a code-escaping hint to the JSON parse error, and update the guidance to keep code out of the modules structure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5685981c99 |
fix(tutorials): repair broken frontend tutorials after UI redesigns (#10255)
* fix(tutorials): repair broken frontend tutorials after UI redesigns Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(tutorials): wait for New menu anchor and drop vestigial async Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
703744fb8b |
feat(ai-sessions): show item preview cards for tools (#10254)
* feat(ai-sessions): show item preview cards for create/update/open-preview tools Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-sessions): only show open_preview card when the preview actually opened Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-sessions): render preview card as a chip on the tool-call header row Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d2c5d6f4b4 |
feat: make content search a full CE feature (#10252)
Content search (the `#` mode of the home-page Ctrl+K search, which searches scripts/flows/apps/resources by content) was capped on CE to 10 scripts and 3 each of flows/apps/resources, with an "EE feature" warning in the UI. It is now a full CE feature: the CE result caps are lifted to match the previous EE limits (10000 scripts, 1000 each of the rest) and the EE warning is removed. Fixes WIN-2218 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
50f1375f18 |
chore(main): release 1.765.0 (#10223)
* chore(main): release 1.765.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
ec6324409d |
fix(s3): support instance-policy credentials in object storage tests (#10238)
* fix(s3): cache ambient aws credentials and surface credential chain errors Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCczeVBDhDnLXWqxPWw6od * fix(frontend): clarify that object storage connection tests run on the server Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCczeVBDhDnLXWqxPWw6od * fix: render test connection tooltip in popup and harden cache test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCczeVBDhDnLXWqxPWw6od * better doc * fix(frontend): correct tooltip wording, tests run from the executing worker Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCczeVBDhDnLXWqxPWw6od * fix(s3): single-flight ambient credential provider creation * fix(frontend): clarify object storage tests run on the server process * fix(frontend): make instance object storage test tooltip provider-neutral --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
2ce21c9ef8 |
feat(git-sync): enable per-item promotion mode on dev workspaces (#10205)
* feat(git-sync): enable per-item promotion mode on dev workspaces Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style: keep unrelated git-sync Alert copy at its original wrapping Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): fall back to parent_path on empty deploy path + bump ee ref computeGitSyncDeployBranch used ?? so a backend-serialized empty path (rename out of the repo filter) skipped the deploy branch and could commit to the tracked base; use || to fall back to parent_path like the backend. Bumps ee-repo-ref for the single-object promotion_open_prs fix (windmill-ee-private#679). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): route dev-promotion non-branchable objects off the tracked base user/group objects (and any unresolvable ref) returned null in promotion mode, so a dev-workspace deploy pushed them straight to the parent's tracked branch. Fall back to the dev's env-label branch instead; the backend opens no PR for them (isolated, not promoted). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(git-sync): dev-workspace promotion via a toggle on the inherited repo A dev workspace reuses the single repo it inherited from prod: a 'Promote to prod via Git' toggle flips it between sync mode (deploys to the dev branch) and promotion mode (per-item wm_deploy/** PRs to prod), with a per-item/per-folder sub-toggle. Removes the redundant separate-promotion-repo setup for dev workspaces. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(git-sync): dev-promotion regression test + widen git_sync_e2e path filter Adds a CLI integration case covering dev-workspace promotion (script -> wm_deploy branch; user/group -> env-label branch, main never touched). Widens the git-sync-test.yml relevance filter to the deploy-branch derivation, git-sync guard, and CLI git-deploy files so the e2e suite runs on PRs like this one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): gate dev promotion toggle on EE, fix card mode + workflow path filters Codex review: (1) show the dev promotion toggle only under an active EE license and revert the optimistic save if the backend rejects it; (2) derive the dev card's display mode from use_individual_branch so promotion copy shows in promotion mode; (3) mirror the new relevance paths into the workflow's top-level push/pull_request filters so it actually triggers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): only use the single-card dev promotion UX when the dev has one repo Codex review: an attached dev workspace keeps its own repositories rather than inheriting prod's. Gating the single-card + toggle + hidden-secondaries UX on repositories.length <= 1 makes a multi-repo attached dev fall back to the normal layout, so no active repo is hidden and an unrelated repo isn't presented as prod's promotion target. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): runtime EE-plan gate for promotion mode, consistent with auto-pull/PR Codex review: promotion mode only had the CE compile rejection, while auto-pull and PR creation runtime-gate on the active plan (check_git_sync_ee_license). Add check_promotion_license and call it from both edit_git_sync_config and edit_git_sync_repository, plus the matching CE rejection on edit_git_sync_config so the two endpoints are symmetric. Promotion is now gated like every other git-sync EE setting. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): dev promotion must reuse the parent workspace's repository Codex review: repository count doesn't prove a dev inherited prod's repo — an attached dev keeps its own. check_dev_promotion_targets_parent_repo resolves the promotion repo's URL and rejects enabling promotion unless it matches one the parent (prod) tracks, so branches/PRs can't target an unrelated repository. Called from both git-sync edit endpoints. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): dev promotion save-time check uses shared parent-repo matcher (url+branch) Delegates to windmill_common::git_sync_ee::dev_promotion_target_matches_parent so the settings gate and the deploy-time safety net share one url+branch identity check. Bumps ee-repo-ref for the EE deploy-time enforcement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump ee-repo-ref for private resolve_repo_url_and_branch Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump ee-repo-ref for promotion-target matcher authz doc Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(git-sync): bump hub scripts to gitsync-cli versions, fix promotion tooltips Point LATEST_GIT_SYNC_SCRIPT_PATH (28790 -> 28796) and GIT_SYNC_PULL_SCRIPT_PATH / gitInitRepo (28789 -> 28795) at the hub versions pinning windmill-cli@1.763.1-gitsync.0, which carries the dev-workspace promotion routing. Slugs unchanged, so the GitHub-App token check and hub script cache are unaffected. Tooltips: enabling promotion pushes a PR-ready wm_deploy/** branch; Windmill only opens the pull request itself when automatic pull requests are enabled. Reword both toggles to stop promising a PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): dev promotion mirrors to the env-label branch, PR toggles exclusive by branch type Bump ee-repo-ref for the dispatcher changes: a promotion dev's deploys now also push to its env-label branch (one extra mirror job per batch, users/groups mirror-only), and `fork_open_prs` no longer applies to a dev in promotion mode where `promotion_open_prs` governs. Frontend: the fork-PR toggle tooltip states its actual coverage (wm-fork/** and the dev branch of a dev workspace) and that a promotion dev's own pull request toggle takes over for wm_deploy/** branches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): reject dev promotion on pre-28796 pinned sync scripts An older pinned sync script bundles a CLI that force-disables per-item branches on every fork, so enabling promotion on a dev workspace with such a pin would silently keep deploying to the env-label branch. Both git-sync edit endpoints now reject the combination with an actionable error; the EE dispatchers (via ee-repo-ref bump) demote inherited configs to promotion-off semantics so markers, branch keys and the mirror match the branch the CLI actually pushes. Roots and auto-managed repositories are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): serialize dev promotion toggle saves The promotion and per-folder toggles persist immediately via whole-repo saves; leaving them interactive while one is pending lets rapid flips race, and the earlier save (enabling runs extra backend checks) can commit last, silently reversing the state the UI shows. Both toggles now disable while a save is in flight. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): lock auto-PR toggle during promotion save, rename-out branch routing Frontend: the automatic-PR toggle is revealed by the promotion toggle's in-flight save; an edit made mid-save was absorbed into the saved baseline without reaching the backend. It now disables during that save. EE (ee-repo-ref bump): dispatcher debounce/concurrency keys and PR markers follow the CLI's parent_path fallback for rename-out items, so their wm_deploy/** branches debounce per-branch and open their PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(git-sync): condense comments to durable constraints Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 8bf73f803158bcbf7b8d55a36f4a1ebfcc1bbcd9 This commit updates the EE repository reference after PR #679 was merged in windmill-ee-private. Previous ee-repo-ref: c2cd718cb53d234f909f485bd7cd43ed9605ffd1 New ee-repo-ref: 8bf73f803158bcbf7b8d55a36f4a1ebfcc1bbcd9 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> |
||
|
|
9739d5a2c2 |
feat: unified read-only diff chat tool (drafts, fork vs parent, search) (#10211)
* feat(frontend): unified `diff` chat tool with cached snapshot, fork mode, and search One read-only global-chat tool for every comparison: drafts vs deployed (workspace index + per-item unified patches over stable YAML), deployed fork vs parent workspace (against="parent_workspace", sharing the fork banner's compareWorkspaces fetch through a single-flight store), and a literal grep over changed diff lines. Multi-file raw apps split into per-file text patches with folder-style index children and per-file reads. Patches are materialized once into a per-workspace cache keyed on draft created_at / comparison ahead-behind markers and the workspace drafts version, so repeated queries never refetch unchanged content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai_evals): teach the mock draft backend what the diff tool reads The diff tool reads drafts through the get_draft overlay, the drafts listing's draft_only flag, and per-row created_at change markers — none of which the benchmark mock modelled (fixed timestamp, always draft_only, overlay ignored), so in evals every draft looked absent and the model looped to max turns. Mirror production: monotonic deterministic created_at bumped per upsert, draft_only computed against the deployed stores, and draft/no_deployed overlays on script/flow/app reads (404-shaped not-found). Also drop the diff case's judge items about conversation content the judge never sees — tool usage is already enforced deterministically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): diff tool reads unsaved editor state instead of going stale The pre-diff flush honors the auto-save toggle (a read-only tool must not persist parked edits), which left a gap: with auto-save off — or after a failed save — the persisted draft the diff reads is stale, and a brand-new editor-only draft looks absent. Item reads now detect unflushed parked edits (hasUnsavedDisabledChanges / failed save state) and diff the in-memory editor value directly, bypassing the snapshot cache (it must only hold persisted state) with an explicit unsaved- changes note; index and search modes warn which items' unsaved edits they exclude. Local values are canonicalized onto the persisted draft shape so they never diff noisily against the deployed side. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(ai): invalidate diff cache the moment any draft write lands The snapshot cache leaned on time windows (5s listing throttle, 15s read reuse) to notice writes it didn't trigger itself — an editor autosave landing between two diff reads could serve the pre-edit patch. The syncer now exposes onAnySaved (fires for landed upserts AND deletes, all keys), and the snapshot subscribes once: a landed write marks exactly that item's patch stale and expires the listing throttle, so the next read refetches regardless of any reuse window. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): reject the diff file arg on single-document items Passing file for a script/flow/classic-app diff was silently ignored and returned the whole patch — an explicit error steers the model to call again without it. Also declares the file arg on the item handlers' signatures it was already flowing through. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): surface empty-file additions/deletions in app diffs An empty file appearing or disappearing produces no text patch, so the per-file split dropped it — a draft whose only change was such a file read "unchanged". Presence changes now keep their added/deleted entry (patch '', 0 lines), render as "(empty file)" in summaries, and a file read states the presence change instead of an empty window. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): include classic-app drafts in the diff index and fix ++/-- search itemTypeForKind now maps classic `app` draft rows to the chat app type (mirroring the read path, which already pairs app/raw_app), so their diffs materialize in the index and search instead of reporting "not addressable". Changed-line search is hunk-aware: `---`/`+++` file labels only occur before the first @@ marker, so a changed source line like `++counter` (rendered `+++counter`) now matches instead of being mistaken for a label. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): mask every variable value in chat diffs; compare classic apps value-to-value Variable VALUES never reach a tool result — the chat-wide invariant read_workspace_item enforces, not just for secrets. Draft-mode diffs mask both sides with a placeholder pair that still marks WHETHER the value changed; fork-mode masks at fetch (and still never decrypts); item reads carry an explicit note. The former secret-only flag is now valueMasked. Classic-app drafts hold the bare grid value while the deployed row nests it beside summary/policy — diffed raw, a one-field edit read as a whole-document rewrite. Both sides now reduce to { value } via classicAppDraftValue (pure, unwraps legacy wrapped drafts), which also cleans the CompareDrafts drawer for classic apps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): honest secret-draft reporting, classic-app metadata split, glob-safe file subjects A secret variable's sides are both masked upstream, so an empty patch cannot prove the value is unchanged — such drafts now report "cannot be compared; may differ" (valueUncomparable) instead of "matches deployed", in the index and item reads. Classic-app drafts mirror summary/draft_path into the bare grid while the deployed row keeps summary as a column: sides now reduce to {summary, value} via classicAppDraftParts, applied to both sides, so a summary edit diffs as one and draft-only markers never pollute the grid diff. Raw-app search subjects strip the file key's leading slash so slash-anchored globs like f/x/*.tsx match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): classic-app local edits, comparison-relevant fork fields, conflicts as unflushed The chat app type spans two draft kinds: item mode now flushes and probes both raw_app and classic app keys, and the flush sweep includes classic-app editor cells (kept out of GLOBAL_DRAFT_KINDS so clearGlobalDrafts never clears an open classic editor). Fork projections gain the fields the backend comparison counts that getItemValue drops: flow schema (with a taxonomy-agnostic inline-hash strip) and resource-type description/format_extension/is_fileset. Folder display_name is not exposed by the API's Folder type, so it cannot be projected. A conflicted save leaves its payload parked with state 'none', so index/search now count conflicts among unflushed paths and say so in their warnings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): staged app renames diff as path; flush classic-app cells at their real keys A staged rename (draft_path) changes where deploy lands an app, so both app kinds now compare `path` on both sides — a rename-only draft diffs instead of reading "matches deployed". classicAppDraftParts returns the staged path separately from the grid. Item mode resolves each draft kind's own storage path and additionally asks the listing which row owns a friendly/renamed path — a renamed classic app's cell lives at its ORIGINAL storage path, which only the listing knows — so pending/failed/auto-save-off edits are flushed and probed at the real keys. The appDiffSides rationale comment is compressed to the repo's four-line limit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): never claim folder parity the API cannot prove folder.display_name exists only as a DB column — no folder endpoint returns it — so an identical projection cannot prove a fork folder matches its parent. Fork index and item reads for folders now say the display name is not exposed and may be what differs, instead of "content matches parent". Exposing the field on getFolder is a backend follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): gap-free patch pagination; forced-fresh comparisons never join older fetches When the char backstop cut inside a patch window, the continuation offset still pointed past the requested window — silently skipping the undelivered lines forever. windowPatch now cuts at the last complete line and continues exactly there (a single over-budget line is delivered truncated and stepped past so pagination always advances). fetchWorkspaceComparison treats an in-flight request as being as old as its start: maxAgeMs now gates joining it, so a freshness-forced post-mutation read (maxAgeMs 0) always issues its own fetch instead of adopting a tally that began before the mutation, and a superseded request can no longer clobber a newer cached result. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): generation-ordered comparison writes; path-only fork reads for enum-less kinds Concurrent comparison requests can share a Date.now() value, letting a superseded request's late result overwrite a newer one and be reused for 30s — cache writes are now ordered by a monotonic request generation. Test pins the same-millisecond race with the newer request resolving first. Fork comparison kinds outside the chat type enum (folder, resource_type, …) were listed and even advertised as readable but no call could reach them: a fork item read without `type` is now a path-only wildcard (ambiguous paths list their kinds and ask for type), messages label entries by their comparison kind, and pending index lines for enum-less kinds advertise the path-only read. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): grid-based wrapper detection, comparison invalidation on mutations, multi-kind wildcard reads, honest hidden-diff summaries The classic-app wrapper heuristic keyed on metadata keys the editor mirrors into every bare grid — a grid with a component named `value` was reduced to that component. `grid` presence is the discriminator: a bare App always has it, a legacy wrapper never does. invalidateWorkspaceDrafts now also drops cached fork comparisons for the workspace, so the FIRST post-deploy fork read cannot reuse a banner-prewarmed pre-deploy tally (the snapshot-baseline check only covered subsequent reads). Wildcard fork reads return a section per matching kind instead of an unactionable "pass type" for kinds the chat type enum cannot name, and the fork index never summarizes ACL-hidden differences as parity — hidden counts stay directional (a conflicted item counts in both). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): address cubic review batch — invalidation scope, races, edge output Comparison cache: invalidation matches either side of the pair (a parent deploy moves its forks' tallies), fences in-flight requests (no new joins, late results rejected via a per-key generation floor), and the map is LRU-capped. Eviction moves from every drafts-version bump to deploy success only — draft saves never move the deployed tally. Fork snapshots also baseline the PARENT's drafts version. Draft materialization carries a stale-generation token so a save landing mid-fetch discards that run's pre-save result instead of repopulating the invalidated entry; a save/delete also expires the fork cache's hasLocalDraft join. onAnySaved listeners are error-isolated (a throwing listener must not mark a committed save failed) and the pagehide keepalive flush notifies them on dispatch. Output edges: folder fork lines drop the empty parenthetical, and a patch-window offset past the end reports itself instead of an impossible range. The eval case pins the diff call's path argument. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(ai): one fencing primitive per cache instead of per-surface races Review rounds kept finding pairwise races between async producers and invalidation — each patched with its own fence. Replace the class: - diffSnapshot: a per-workspace mutation epoch, bumped by every invalidation. Both reconcilers run a bounded retry loop — joiners re-validate after awaiting, producers refuse to store results whose inputs predate a mutation. Covers in-flight listing adoption and pre-deploy fork tallies in one mechanism. - workspaceComparison: per-WORKSPACE generation floors (either side of a pair). Any request started before an invalidation is fenced from joining and from landing in the cache — including superseded requests the inflight map no longer tracks. Also: delete_workspace_item invalidates comparisons like deploy does (deployed state moved), and empty FILTERED indexes say the filter matched nothing instead of claiming workspace/fork parity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): invalidate comparisons on every direct deploy; keep secret caveat with metadata changes Direct chat deploys (schedule/trigger/resource/variable/app) bypass deployDraftToWorkspace and never evicted cached fork comparisons — the shared deploy tail now invalidates before the fallible draft cleanup. A secret variable whose metadata also changed produced a non-empty patch that silently dropped the value-uncomparable caveat; item reads, the index, and fork sections now keep the caveat alongside the patch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): scope diff caches to the authenticated identity; derive fork freshness from the comparison store An SPA logout/login left workspace-keyed diff caches (per-user drafts, permission-filtered fork patches) readable by the next account — both cache modules now wipe on identity change, with a global generation floor fencing requests started under the previous account. The fork snapshot stamped its own fetchedAt over a comparison that could already be near expiry, compounding the two 30s windows, and survived comparison-store invalidation when draft cleanup failed after a deploy. It now carries the comparison's own fetchedAt/generation and stops reuse the moment the store fences it. Delete-item invalidation moved before the fallible draft cleanup for the same reason. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): fence fork-reconciliation joins across account switches ForkCache lacked the epoch stamp WorkspaceCache carries, so a joiner arriving after an identity change (or any epoch bump landing before it) compared its own post-bump epoch against itself and adopted the old producer's in-flight tally. The cache now records its producer's epoch for the joiner and reuse gates, and an identity change also discards the in-flight reconciliation maps so no cross-identity join exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): surface swallowed fork-side fetch failures; include conflicted editor edits in item diffs The shared getItemValue reads {} for any failed fetch, so a transient API failure on a fork side rendered as a fabricated one-sided diff (or parity when both sides failed). A fork side is only fetched when the comparison lists it as existing, so an empty read now raises and shows as a fetch-error entry. Item reads promised conflicted local edits (the index says so) but the local-override branch only covered autosave-off and failed saves — a conflict silently fell back to the persisted draft. Conflicts now read the in-memory editor value too, with a caveat naming which side is shown either way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): report failed diff materializations as unsearched instead of silently omitting them A side-fetch failure left an index entry with status 'error' and no patch; diff search skipped it and still presented definitive no-match or complete-count results. Failed entries are now listed in a warning naming what was not searched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d6cf1ef987 |
feat: attach text files to chat messages, read on demand via file tools (#10215)
* feat: attach text files to chat messages, read on demand via file tools
* fix: resolve name collisions and sync message files with the transcript
* refactor: render message file chips with the shared context badge
* fix: suffix same-name attachments, sync registry on compaction, bound file bytes
* feat: carry message files across compaction, drop them from the roster
* refactor: merge context, dom and file badges into one wrapping row
* fix: dedupe identical attachments and make badge list keys collision-proof
* fix: name carried files inside the collapsed summary instead of badges
* fix: serialize registry reconciliation and correct the attachment budget
* fix: reserve pending bytes so overlapping reads honor the attachment budget
* fix: share attachment byte budget across concurrent composers
The bottom composer and the edit box are both mounted while editing an
earlier message, but each enforced MAX_CONVERSATION_FILE_BYTES against
only its own staged files plus the transcript. Two attaches near the cap
could each pass independently and overflow the persisted transcript.
Each composer now publishes its staged bytes (committed attachments +
in-flight reads) to the manager, keyed per instance, and the attach-time
budget subtracts every other live composer's stage. A message an open
composer is editing is skipped from the transcript sum since that
composer's stage stands in for it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: charge an edited message at its persisted size until the edit commits
An edit is not committed until send, so the edited message's persisted
attachments return if the edit is cancelled. Substituting only the edit
box's (possibly emptied) stage let the always-mounted bottom composer
claim headroom that vanishes on cancel: remove the near-limit files in
the editor, fill the bottom draft, cancel, and the persisted transcript
overflows MAX_CONVERSATION_FILE_BYTES.
attachmentBytesExcluding now charges a message another composer is
editing at max(persisted size, editor stage), so freed space only
becomes available once the edit actually commits.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: preserve a message file's exact name when a session row clashes on rebuild
syncMessageScoped rebuilds message-scoped rows from the transcript through
collision-suffixing addFiles. Session rows load first (on restore), so one
holding a wanted name pushed the rebuilt message row to a "(2)" suffix while
the persisted prompt still referenced the bare name — get() then resolved the
reference to the session asset and the model read the wrong content.
Free the name from the conflicting session row before the rebuild so the
message row reclaims its exact reference. The rename is in-memory only: it is
deterministic and re-applied on every load, and the session roster is
regenerated live each send, so the session asset stays addressable under the
suffix without a persisted-record update.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: reserve resent files across the edit-resend gap
The edit box unmounts (dropping its staged-byte entry) the instant the user
submits an edit, but restartGeneration then awaits registry sync and beforeSend
before the optimistic bubble lands in the transcript. During that gap the
resent files were reserved nowhere, so the always-mounted bottom composer could
attach into the temporary headroom and the resend would then push the persisted
transcript past MAX_CONVERSATION_FILE_BYTES.
restartGeneration now reserves the resent files' bytes in shared manager state
before the transcript slice; sendRequest releases the reservation once it
installs the bubble (or restores the files to the composer on a pre-install
bail). The reservation bridges the gap so the budget stays honored throughout.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: re-target an in-flight index when a session row is renamed on rebuild
#freeNameForMessageRow renames a restored session row so a same-named message
attachment can reclaim its exact name. But the row's #indexFile, started under
the old name during restore, stamps via #patchFile(oldName, file) — after the
rename that no longer matches, leaving the row stuck 'indexing' so read_file
rejects it and search_files excludes it.
Re-kick #indexFile under the new name when the renamed row is still indexing;
the stale completion then no-ops (its name is gone).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: release resend reservation on local-command sends; surface compaction-orphaned files
Two follow-ups to the message-attachment work:
- A resend edited to /clear or /compact runs the local-command path and returns
before installing a bubble, so the #RESEND_KEY reservation set by
restartGeneration was never released and its bytes stayed charged, blocking
later attachments. Release it on every sendRequest path that exits before
install (via #releaseResendReservation).
- Drop-oldest compaction (summary fallback) removes API messages without a
summary, so a folded message's `## ATTACHED FILES` reference no longer reaches
the model even though the file stays readable. The roster omits message-scoped
files, so the model loses awareness of them. orphanedMessageFileNames() finds
message files whose only referencing message went negative-index, and the
roster now advertises them (summary compaction already carries its own).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: key the resend reservation per send so unrelated sends can't release it
The resend reservation used a single shared key, so a normal or concurrent
sendRequest released it at its own install/early-return even though it didn't
own it — dropping an in-flight resend's reservation and letting attachments
staged before the resend bubble lands under-count against the byte cap.
restartGeneration now mints a per-resend token, reserves under it, and threads
it through sendRequest as resendReservationKey; releases act only on that key.
A send with no token (every normal send) releases nothing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: reserve the whole wanted-name set when freeing session rows on rebuild
Freeing a session row for a message-scoped rebuild suffixed it against current
rows only. With the transcript referencing both notes (2).md and notes.md and a
session row named notes.md, freeing notes.md renamed it onto notes (2).md — also
a wanted reference — so that message row cascaded to notes (3).md while its
persisted reference stayed notes (2).md, and read_file returned the session file.
#uniqueName now accepts a reserved set; the rebuild frees each session clash
clear of the entire wanted-name set, so every message row reclaims its exact
reference regardless of collision order.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: orphan summary-carried files when drop-oldest removes the summary
orphanedMessageFileNames classified summary rows as always-live, but a summary
carries its folded files' reference on its own API message. When summary
compaction succeeds and a later summarization fails, drop-oldest can remove that
API message, yet the files stayed off the roster — so the model lost their
reference even though they remained readable.
The summary display row now tracks its API index (slot 0 at creation, re-based by
drop-oldest); a negative index reads as "counterpart gone" and its files move to
the roster, mirroring user-message orphans. The index is used only for orphan
detection, never as a restart target.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: reserve outgoing file bytes for normal and queued sends too
The resend reservation covered edit/retry, but a normal or queued send has the
same gap: the composer (or queue) clears its files the instant sendRequest is
called, dropping the staged-byte entry, while sendRequest then awaits
regrantLocked()/refreshFolders() before the bubble lands. With a locked or slow
linked folder the composer stays enabled, so a fresh drop can spend the same
headroom and overflow the 5 MB cap once the first bubble installs.
Generalize the reservation: sendRequest mints a per-send token and reserves the
outgoing files' bytes just before attachment upkeep (reusing restartGeneration's
token when present), and releases it on install or any pre-install exit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor: join attachment layers on a stable content-hash id
Sixteen review rounds kept finding the same bug family: a message file's
identity was its display name, joined by hand across the registry, the
transcript, the prompt reference, and the render keys — every same-name
collision or interleaved rebuild made two of those copies disagree.
Give each message attachment a deterministic id, attachedTextFileId(name,
content) — a synchronous pure-JS hash (works on plain-HTTP deployments) whose
exact value is pinned by test, since persisted transcripts reference it. The
prompt and roster list the id, the file tools resolve id-first (bare names
remain a fallback for legacy chats and session links), and pre-id transcripts
hydrate on load by recomputing the same hash — no migration state.
Names become display-only and may collide freely, which deletes the machinery
that defended them: the suffix-readback registration loop, session-row renaming
on rebuild (#freeNameForMessageRow, reserved-set #uniqueName), and the
reconciler's serialization guards (#syncSeq/#syncChain) — syncMessageScoped now
compares ids instead of awaiting blob text, so it is synchronous and cannot
interleave. A same-name clash within one draft gets a composer-local courtesy
rename before the id is minted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: resolve bare names to session rows and scope id searches to one row
A bare name is the roster's namespace: session links are advertised by
filename and have no other handle, so a same-named message attachment (which
is addressed by id) must not shadow them. get() now resolves session rows
first, keeping the message-row name lookup only as the fallback for
transcripts persisted before ids existed.
search_files restricted an id reference by mapping it back to the display
name and letting the worker filter on it — same-named files were then
searched together under one label. The tool now passes the resolved row
itself, so an id-scoped search can only ever hit its own file.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: label search hits with resolvable ids, normalize attach batches at commit
An unscoped search_files reported hits by display name only. Names may
collide, so a hit could not be mapped back to the row that produced it —
a follow-up read_file on the bare name could return a different same-named
file. Rows carrying an id are now labeled `name (file id: …)` in hit lines,
so every hit names the reference that resolves to exactly that row.
addTextFiles normalized (deduped, courtesy-renamed) each file against a
snapshot taken during its read loop. Attach batches overlap, so a file
committed by another batch between reads escaped both checks — duplicate or
same-named unsuffixed entries in one message. Normalization now runs in the
single synchronous commit step against the live list (foldIntoDraft), where
nothing can interleave.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: dedupe renamed re-drops in foldIntoDraft, truncate queued file chips
"Same file dropped twice" means same original (name, content), but a
courtesy rename erases the original name — an identical re-drop then missed
the duplicate check and landed as a further-suffixed copy. The dedupe now
also matches entries whose suffix-stripped base name equals the read's name.
Queued file chip labels get min-w-0 so long filenames truncate inside
max-w-36 instead of overflowing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: address cubic review — line counts, chip clicks, reference robustness
Five fixes from the cubic pass:
- The prompt advertised split('\n').length lines, one more than read_file
reports for newline-terminated files — textLineCount now matches the tool's
numbering (0 for empty, no phantom trailing line).
- Clicking a sent message's badge opened edit mode (the wrapper's
click-to-edit), unmounting the preview popover as it opened; the badge row
now keeps clicks to itself.
- resolve() accepts the composite label rosters and search hits print
(`name (file id: x)`) — models echo references verbatim, so the printed
form must resolve.
- fileToAttachedTextFile enforces MAX_TEXT_FILE_BYTES itself (raw size +
decoded byte length), so no ingestion path can persist an oversized
attachment past the composer's pre-check.
- Duplicate detection after a courtesy rename now uses an explicit sourceName
instead of inferring provenance from the display name — a user's real
`report (2).md` is not a rename of `report.md`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: exact names win over label parsing, commit recheck uses decoded bytes
resolve() parsed any `name (file id: x)`-shaped reference as a printed label,
so a session file literally named that way became unreachable by its exact
name (the dead-id fallback resolved the base name instead). Exact id and
exact-name lookups now run before label interpretation.
Attachment admission and the pending reservation use raw File.size, but the
committed charge is the decoded UTF-8 length — malformed input decodes each
invalid byte to a 3-byte replacement character, so a file passing the 8KB
text sniff could inflate past the conversation cap. The synchronous commit
step now re-checks the live budget against decoded sizes
(admitWithinByteBudget) and drops what no longer fits, with the budget toast.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: normalize files folded into the queued message
Repeated submissions during a stream aggregate into one queued message, but
their files were concatenated raw: an identical re-attach duplicated its chip
and ate a slot (possibly displacing a distinct file at the eight-file cap),
and a same-name clash skipped the courtesy rename. The queue now folds new
files through the same commit normalization as the composer — the queued
entry is a message draft like any other.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* style: compaction boundary label uses text-normal text-2xs
* fix: fold provenance survives pass-through, dequeued files fold into the draft
foldIntoDraft recorded sourceName only for renames it performed itself, so a
file already courtesy-renamed by the composer lost its provenance when folded
into the queue — a later re-attach of the original escaped dedupe. Folds now
compose: the original source name rides through every fold, and dedupe
matches on it.
dequeueMessage restored queued files into a possibly-populated composer by
raw concatenation; prependText now folds them like every other draft
aggregation (dedupe, courtesy rename) before applying the cap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: one MessageDraft owns the lanes that ship with a send
Review rounds kept finding the same P2 shape: an aggregation point where
files join a draft (composer commit, queue append, dequeue restore) that
forgot one of the draft rules — fold dedupe, courtesy rename, slot caps,
byte admission, lanes moving together. The rules existed only as convention
re-implemented per site.
MessageDraft owns them once: text, pastes, images, and text files live on
one object with addFiles (fold + optional decoded-byte admission + cap),
addImages (cap), prepend (restore-merge), replaceIfEmpty (occupied-guard
restore), and take (all lanes leave together). The composer holds a draft
instead of four state vars, and the queue is a draft behind the existing
queuedMessage/queuedImages/queuedFiles accessors — an aggregation point can
no longer skip a rule, because there is no raw array to concatenate into.
Deliberately not moved: @context and DOM picks (ContextManager owns their
lifecycle), the conversation byte budget's cross-composer ledger (store-side
follow-up), and sendRequest's options shape (it decomposes immediately and
is pinned by the manager test suite).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: correct the drop-routing comment, condense the budget doc
* fix: address cubic review — name sanitization, drop hold, merge restores
Four fixes from the cubic pass:
- Attachment display names render into model-facing prompt blocks, and OS
filenames may legally contain control characters — sanitizeAttachmentName
strips them at attach and again at every prompt-render site (legacy names
predate the attach-time pass), so a crafted name cannot inject prompt lines.
- Drop routing awaits handle/entry resolution before it can call
addTextFiles; a send during that window landed the dropped files on the
next message. The drop handler now holds sending (holdSendForIngestion,
taken before the first await) until routing completes.
- Restoring a taken queue after a failed auto-send replaced the queued draft
wholesale, silently losing a follow-up queued during the preflight. Both
#restoreQueue and the unmounted-input requeue now merge via draft.prepend —
the taken entry's text lands above the newer follow-up.
- restartGeneration validated the API restart index only after reserving the
resend bytes and truncating the transcript, so a stale index threw with the
reservation leaked and the display transcript half-mutated. The index is
resolved and validated before anything is touched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: restored drafts keep chronological priority, store names stay resolvable
A failed auto-send's restore folded the taken (older) draft's attachments
AFTER a follow-up queued during preflight, so at the slot caps the older
attachments silently dropped despite the text landing first — and only one
entry's pinned context survived. prepend() now puts the restored lanes ahead
(the cap drops the newest additions) and #restoreQueue unions both pinned
contexts by identity.
Session filenames were sanitized only at prompt render, so an id-less file
whose stored name carries control characters was advertised under a name
that resolve() could not match. Names are now sanitized at every store
row-creation site (attach, folder expansion, refresh, and persisted-row
restore for pre-sanitization records), making the advertised name the stored
name everywhere — render-site sanitization remains as defense in depth.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: update ee-repo-ref to aaa6cb89b05b76139252c64f057e53b94d12ac60
This commit updates the EE repository reference after PR #680 was merged in windmill-ee-private.
Previous ee-repo-ref: 4c08634af953db5c1125b1fb03f5af211fe21db3
New ee-repo-ref: aaa6cb89b05b76139252c64f057e53b94d12ac60
Automated by sync-ee-ref workflow.
* chore: repin ee-repo-ref to main's eb3690a34b (aaa6cb89 needs the unmerged AmqpTrigger OSS companion)
* Revert "chore: repin ee-repo-ref to main's eb3690a34b (aaa6cb89 needs the unmerged AmqpTrigger OSS companion)"
This reverts commit
|
||
|
|
7ac27c1ef2 |
fix(frontend): limit compare & deploy rows to the active direction (#10234)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
7fb8a2e390 |
fix(parsers): keep s3 asset path suffix verbatim to preserve storage distinction (#10241)
* fix(parsers): keep s3 asset path suffix verbatim to preserve storage distinction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01An2pTqSmqJd2XwnagvX4kM * package json * fix(pipelines): preserve named storage in generated TS/Python S3 URIs The TS/Python templates emitted `s3:///${s3Key(path)}`, stripping the leading slash and pinning the URI to default storage. For a named-storage asset path (`secondary/key`) that produced `s3:///secondary/key`, which resolves to the default storage with key `secondary/key`, dropping the named-storage dependency and reading/writing the wrong object. Emit the path verbatim after `s3://` (matching the DuckDB template) so a named-storage input/output keeps its storage; identical to the previous output for default-storage paths. Removes the now-unused `s3Key` helper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(cli): align bun.lock parser versions with frontend The PR bumped windmill-parser-wasm-asset (1.749.0→1.753.0) and windmill-parser-wasm-regex (1.692.0→1.764.0) in package.json and the npm package-lock.json for both cli and frontend, but cli/bun.lock was left pinned to the old versions. Sync it so the CLI's wasm asset parser (used by localGraph inference) matches the frontend and deploy-time parser. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
dc5b006e7f | fix npm checks | ||
|
|
68debab877 |
feat(triggers): add AMQP (RabbitMQ) trigger via lapin (#10230)
* feat(triggers): add AMQP (RabbitMQ) trigger using the lapin library Fixes WIN-2214 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(triggers): defer AMQP cross-workspace deploy pending utils-internal publish Revert the amqp_trigger additions to the shared windmill-utils-internal TriggerDeployKind and the frontend cross-workspace deploy adapter: the frontend installs the published npm package, which lacks the new kind until a release is cut. AMQP create/edit/delete/list/sync/capture are unaffected (they use local types); only cross-workspace deploy/merge of AMQP triggers waits on the package bump. Also document the at-most-once ack in the consumer loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): address AMQP review — at-least-once ack, workspace cascade, contracts - ack AMQP deliveries only after successful dispatch; nack+requeue on failure - add ON DELETE CASCADE workspace FK so amqp_trigger rows are cleaned on workspace deletion (and the listener stops) - fix the /amqp_triggers/test OpenAPI body and add amqp_trigger to WorkspaceDiffRow.kind - register AMQP in the generated workspace trigger tool (create_trigger) - drop banned $bindable defaults on optional props in the config section - add build_uri unit tests (encoding, ports, vhost) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): stop AMQP poison-message loop and reconnect on transient drops Chaos testing against a live RabbitMQ broker showed the previous nack(requeue) + immediate re-poll spun a tight redelivery loop (~1000 critical-error reports/sec) on a poison message, and any connection blip permanently disabled the trigger (lapin has no built-in reconnect). - on dispatch failure: nack+requeue then stop consuming; the listener framework re-lists the trigger after its ping goes stale (~15s), backing redelivery off to that cadence instead of a tight loop (verified: rate dropped from ~1000/s to ~1 per ~26s, message preserved) - on connection/stream error: stop and let the framework reconnect instead of disabling; persistent failures are still disabled via get_consumer (verified: a forced connection close now auto-reconnects and resumes) - finish the AI create-trigger action wiring for AMQP: add amqp to CreatedResourceTriggerKind, the action-card registry, and the drawer registry so the result card renders and its "Open" action works Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): complete AMQP frontend registries and defer merge rows - add amqp to capturableTriggerTypes (so AmqpCapture mounts), the Runs jobTriggerKinds filter, and CLOUD_DISABLED_TRIGGER_TYPES - wire AMQP into global AI chat mode: TRIGGER_KINDS, the request union, writeTriggerSchema, triggerServices, and the draft adapter - stop emitting actionable AMQP fork-comparison rows (revert amqp_trigger from TRIGGER_OR_SCHEDULE_TABLES) since cross-workspace deploy is deferred until windmill-utils-internal is published — avoids a deploy that fails with "Unknown kind: amqp_trigger" - use design-system TextInput instead of raw <input> in the config section Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): complete AMQP session/draft registries and constrain prefetch - add amqp to the session-deploy, draft-compare, preview-router, and copilot workspace-item registries so AMQP drafts/deploys/nav/path resolution work - include amqp_count in the MoveDrawer attached-trigger rename warning - replace the raw prefetch <input> with a design-system TextInput bounded to an integer 1-65535 (backend u16) and block save on invalid values Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): make AMQP disconnect/reconnect consistent with the Kafka trigger lapin, like rdkafka, has no transparent reconnect, so the AMQP listener now mirrors the Kafka trigger's explicit reconnect loop instead of relying on the framework re-list (which disabled the trigger once get_consumer failed on a sustained outage): - get_consumer returns cheaply; consume owns a (re)connect loop that retries with a 30s backoff, reports a critical error every 10 failed attempts, and reports a recovered critical error once it reconnects — never disabling the trigger on a connectivity failure - a consumer/stream error breaks out to reconnect rather than disabling - dispatch failure still nacks+requeues (at-least-once) with a short backoff to avoid a tight poison-message loop, keeping the connection alive Verified against a live RabbitMQ broker: killing the broker keeps the trigger enabled and retrying (attempt N), and restarting it auto-reconnects (logs "reconnected after N attempts") and resumes dispatch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): complete AMQP capture registries and constrain prefetch contract - add the 'amqp' case to triggerKindToTriggerType so opening the AMQP editor from a capture button no longer throws "Unknown TriggerKind: amqp" - register AmqpIcon in CaptureTable's icon map and add an AMQP entry to the script/flow CaptureButton menu - bound the OpenAPI prefetch_count to an integer 1-65535 (matches the Rust u16) and regenerate clients/prompts - require a non-empty exchange name when the exchange binding is enabled - build_uri: fall back to "/" on a blank vhost and bracket IPv6 hosts (+ tests) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(triggers): wire AMQP into pipeline graph, git-sync, and preprocessor types - asset_graph: discover attached amqp_trigger rows and emit an AMQP TriggerEdge so AMQP triggers render (and can be opened/deleted) on the data-pipeline canvas - frontend pipeline graph: add amqp to NativeTriggerKind, the add-trigger menu, node presentation, event-trigger set, annotation keywords, and the editor/service registrations - git-sync: add the amqp_trigger include pattern (+ test) so an AMQP git-sync deployment stages only its .amqp_trigger.* file, not an unrelated same-path object - preprocessor starters: add the AMQP event to the generated TS/Python/PHP trigger event types (kind/payload/exchange/routing_key/queue_name/redelivered/ delivery_tag) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): finish AMQP pipeline/parser wiring, prefetch validation, source lists - fix a stray edit that corrupted the pre-existing MqttTriggerEditor import ($lib/... path) in PipelineTriggerEditors.svelte - reject prefetch_count = 0 server-side in validate_config (RabbitMQ treats 0 as unlimited) and defensively skip basic_qos(0) in build_consumer (covers the capture path that bypasses CRUD validation) - recognize `// on amqp` in the canonical parser (TriggerSpec::Amqp) and add amqp to the CLI non-autorun/event-trigger sets so a pipeline cascade never runs an AMQP-only node as a manual root without an event - add amqp to the preprocessor intro lists and both pipeline AI instructions Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): reject zero AMQP prefetch in all paths and finish guidance lists - extract a shared validate_amqp_options used by both CRUD validate_config and build_consumer, so capture configs (which bypass CRUD validation) also reject prefetch 0 instead of silently connecting with an unlimited buffer (+ unit tests for 0/1/65535/None) - add AMQP to the main script-writing preprocessor-sources prompt and the CLI triggers-skill guidance list Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(triggers): de-duplicate AMQP prefetch comment and fix GET response text - keep the zero-prefetch rationale only on the shared validate_amqp_options doc; drop the redundant call-site comments - correct the getAmqpTrigger OpenAPI 200 description ("deleted" -> "retrieved") Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to aaa6cb89b05b76139252c64f057e53b94d12ac60 This commit updates the EE repository reference after PR #680 was merged in windmill-ee-private. Previous ee-repo-ref: 5da5fd65aca9594b2611837a52e4677b544b0380 New ee-repo-ref: aaa6cb89b05b76139252c64f057e53b94d12ac60 Automated by sync-ee-ref workflow. * chore(migrations): consolidate the four AMQP migrations into one The table and the three enum ADD VALUE statements (trigger_kind, job_trigger_kind, draft_kind) are one atomic feature. ALTER TYPE ... ADD VALUE runs inside the migration transaction on PG >= 14 (Windmill's minimum) since the amqp_trigger table doesn't reference those enum types, so they can share a single migration instead of four. Verified applying cleanly in a single transaction on a fresh DB. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- 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> |
||
|
|
c6fd6da971 |
repoint slack hubPaths to form-data 4.0.6 versions (#10239)
The image prebundles the hub scripts listed in hubPaths.json by their pinned (immutable) version. windmill-integrations republished the four image-cached slack scripts with form-data bumped 4.0.5 -> 4.0.6; point hubPaths.json at the new versions so freshly built images cache the fixed lock instead of the stale one. Fixes part of #10219 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d24e176816 |
fix(parser): spurious pg arg inferred from placeholders in comments (#10226)
* fix(parser): ignore pg placeholders in comments, strings and dollar quotes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NXWRDKzswMeDBP5THXpb9Q * package json + lock * chore: remove stray root npm lockfiles Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kuUhCPD9ph4ZuFd13SCWJ --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b0bf25683b |
fix(frontend): prevent browser back-swipe navigation over monaco editors (#10229)
* fix(frontend): prevent browser back-swipe navigation over monaco editors Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EEP1T4UogYABBifdNJUUuQ * fix: toggle root overscroll-behavior-x on editor hover instead of cancelling wheel events Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EEP1T4UogYABBifdNJUUuQ * refactor: replace per-editor swipe guard action with single global handler in root layout Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EEP1T4UogYABBifdNJUUuQ * style: compress swipe-guard comment to four lines Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q97WNWUT9mntEEVhg6kpzv --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
32994df427 |
fix: return to parent workspace when a fork is deleted remotely (#9898)
* fix: return to parent workspace when a fork is deleted remotely When a workspace fork was deleted remotely while a user had it open, reloading stranded them: a regular member got logged out (whoami fails on the vanished workspace) and a superadmin silently landed on a dead workspace whose requests 404. Detect the deleted fork on load and redirect to its parent (or the workspace picker) with a toast instead. - forkParentMemory.ts: persist a bounded fork->parent map in localStorage while a fork is reachable (the parent is unrecoverable post-deletion). - (logged) layout: record the current fork's parent via an effect. - root layout: tryRecoverFromDeletedFork detects the vanished fork in loadUser and redirects to the remembered parent or the workspace picker, reusing the workspace list already fetched on mount. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: verify fork existence for superadmins before deleted-fork redirect * fix: only recover deleted fork on actual 404 from superadmin check * fix: recover prefixless dev-workspace forks via remembered parent * fix: use workspace exists check to detect deleted forks * fix: restore wm-fork- detection for non-member superadmin forks * fix: record fork parent for non-member superadmin dev workspaces * Update frontend/src/routes/(root)/+layout.svelte Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * fix: store fork parent map with null-prototype to handle __proto__ ids --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> |
||
|
|
9bc1f62128 |
feat(ai): session chat nits — empty sends, command picker polish, session-state prompt (#10233)
* feat(ai): session chat nits: empty sends, picker polish, session state * fix(ai): scope empty-send turns to global chat and pin new behavior * fix(ai): align grouped search nav with display order, enable empty-send button * fix(ai): fork fallback for unlisted workspaces, section headers across branches * style(ai): hint-colored 3xs picker section headers, drop inline row descriptions * style(ai): more spacing between picker sections * fix(ai): require context elements for empty global-chat sends * style(ai): no empty bubble for text-free messages * fix(ai): review round 2 — keyboard tooltip access, requestedMode guard, no display names in prompt * fix(ai): queue context-only drafts pressed while a response streams * fix(ai): shared context identity for queued badges and full queue-context union |
||
|
|
572d69e5ae |
feat(ai): open the Compare & Deploy page from chat with item preselection (#10232)
* feat(ai): open the Compare & Deploy page from chat with item preselection * fix(ai): label the compare link card outside sessions * fix(ai): scope untracked-chat compare links to explicit items * style: drop narration comment on compare mask precedence * fix(ai): match compare items mask against parked live-draft paths * fix(ai): land maskless-mode compare on the view holding the masked drafts * fix(ai): honor explicit fork mode over the draft-mask heuristic * docs(ai): describe mask-aware compare mode auto-pick * fix(ai): match legacy app fork diffs under their identity mask key |
||
|
|
28966bdbf1 |
fix(frontend): curl fallback for +Variable/+Resource in bash sandbox mode (#10235)
* fix(frontend): use curl fallback for +Variable/+Resource in bash sandbox mode When a bash script uses `# sandbox <image>` or `# docker`, the body runs inside a custom container image that does not have the `wmill` CLI installed, so the `wmill variable get` / `wmill resource get` snippets inserted by the +Variable and +Resource pickers fail. Detect `# sandbox`/`# docker` in the editor code and insert a curl-based snippet using the BASE_INTERNAL_URL, WM_TOKEN and WM_WORKSPACE env vars (available in sandbox) instead. Fixes WIN-2215 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): mirror worker grammar and use curl/wget fallback in sandbox mode Address review of the bash sandbox picker fallback: - Extract detection into `bashRunsInCustomImage`, mirroring the worker's BashAnnotations grammar (leading comment lines only; `# sandbox <image>` or bare `# docker`). A bare `# sandbox` is the nsjail-bash modifier that still runs on the worker rootfs where `wmill` is available, so it now correctly keeps the `wmill` snippet. This also fixes the substring false positives (`# sandboxed`, `# docker` in prose/body) and false negatives (`#sandbox <image>`). - The default `# sandbox alpine:latest` image ships busybox `wget`, not `curl`, so the snippet now tries `curl` then falls back to `wget`. - `variables/get_value` returns a JSON-quoted string; strip the outer quotes with `sed` so the sandbox snippet matches the `jq -r .value` output of the non-sandbox branch. Resources return JSON either way. - Add focused unit tests for the detection grammar. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fd51d40f12 | feat(pipelines): catalog declared measures and dimensions (#10190) | ||
|
|
858d9a5527 |
chore(main): release 1.764.0 (#10196)
* chore(main): release 1.764.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
ddec2abbb3 |
feat(jobs): cap total queued jobs per workspace on cloud (#10218)
* feat(jobs): cap total queued jobs per workspace on cloud A workspace could flood the queue with an unbounded number of jobs across many concurrency keys and scripts (or keyless jobs), which the per-key cap from #10197 does not bound. Add a companion instance-wide ceiling on a workspace's total queued jobs. check_workspace_queue_cap rejects a push once the workspace has WORKSPACE_MAX_QUEUED_JOBS (default 20000, superadmin-configurable, 0 to disable) jobs queued, cloud-only and runtime-gated on CLOUD_HOSTED like the per-key cap. It runs on every push, so it applies even to premium workspaces and catches parallel for-loop floods. Jobs already queued still drain; only new pushes past the ceiling are rejected, so an in-flight flow only fails to push further work while at the ceiling. The setting loader self-gates on CLOUD_HOSTED so it is never loaded off cloud, from initial load or a settings-change reload. The depth count is bounded by the cap via LIMIT so a runaway backlog never costs an unbounded scan on the push path. * docs(jobs): note the workspace cap is a soft ceiling and the depth helper is count-only Records the two review points as constraints: the cap does not serialize admission (a soft ceiling by design, like the per-key cap), and workspace_queue_depth is pub only for the test, returns a count not job data, and leaves authorization to the caller. |
||
|
|
11fda89b52 |
feat(telemetry): generic feature-usage telemetry with AI session metrics (#10200)
* feat(telemetry): add generic feature_usage table and batched logging endpoint Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(telemetry): log AI session usage events and document them in telemetry settings Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): use escape sequence instead of literal NUL bytes in buffer key Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): validate dimensions, decouple retention, keepalive flush Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): allowlist feature-usage dimensions and index retention scans Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): pin tool-name allowlist and deploy session attribution Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(telemetry): route AI chat usage through feature_usage and drop ai_chat_usage Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(telemetry): slim dimension validation to registered kinds plus key shape Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): backfill ai_chat_usage into feature_usage before dropping it Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): disclose provider and model identifiers in telemetry settings text Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): issue all flush chunks before awaiting so pagehide keeps them Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: update ee-repo-ref to 6306c072a50937ea9af44a5bcf42345543207486 This commit updates the EE repository reference after PR #672 was merged in windmill-ee-private. Previous ee-repo-ref: 964f242a0eb44db7f7d26636cc8d76aeabea2b73 New ee-repo-ref: 6306c072a50937ea9af44a5bcf42345543207486 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Fable 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> |
||
|
|
11bb37d7ca |
fix(forks): show workspace settings link in sidebar for fork creators (#10216)
* fix(forks): show workspace settings link in sidebar for fork creators PR #10166 grants a fork's creator developer-management access to the workspace settings page, but every sidebar entry point to /workspace_settings stayed gated on is_admin || superadmin, so a non-admin fork creator could only reach the page by typing the URL. Widen the four sidebar guards (SettingsMenu, SidebarContent, WorkspaceMenu, WorkspaceScopeHeader) to also admit fork owners, via a shared isForkOwner() helper mirroring the fork-owner check already on the settings page and the backend authorize_fork_owner_add_user grant. Fixes WIN-2210 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(forks): check fork ownership against the settings entry's target workspace In session mode SettingsMenu's entry targets workspaceSettingsTarget, which can differ from the active workspace, but the fork-owner guard was checked against the active workspace — hiding the entry when the session targets the user's fork, or exposing a dead link when only the active workspace is theirs. Check ownership against the workspace the entry actually points at. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
87be041c09 |
fix(git-sync): avoid percent-encoded colon in git-sync hub script path (#10213)
* fix(git-sync): avoid percent-encoded colon in git-sync hub script path The git-sync init/detection hub script slug contained a colon stored as `%3A` in the run-by-path URL. The generated API client re-encodes path params with encodeURI, turning `%3A` into `%253A` (double-encoding). Some hardened reverse proxies / WAFs reject double URL-encoding and return a bare 400 before the request reaches Windmill, breaking git-sync repository detection on those instances. The hub resolves scripts by numeric id and ignores the slug, so dropping the colon from the slug is behavior-neutral (same script, same id-keyed worker cache) while producing a colon-free run URL. Also force-cache GIT_SYNC_PULL_SCRIPT_PATH at build alongside LATEST_GIT_SYNC_SCRIPT_PATH so the backend-driven pull script is always baked into the image for airgapped workers, instead of relying on an incidental hubPaths.json overlap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump ee-repo-ref for git-init slug match fix Pulls in windmill-ee-private#676 so the EE is_git_init_script check matches the colon-free git-init hub slug (GitHub App token grant for git-sync jobs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump ee-repo-ref for git-init slug helper + test Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to a3adea1ffb406e709cc480871df58fab6c51aca1 This commit updates the EE repository reference after PR #676 was merged in windmill-ee-private. Previous ee-repo-ref: cef4e008ef62dec434aa9bb3ec783db8aff6a1c1 New ee-repo-ref: a3adea1ffb406e709cc480871df58fab6c51aca1 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> |
||
|
|
0593ff7d7d |
feat(ai): add npm package search tool to global chat (#10204)
* feat(ai): add npm package search tool to global chat Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): encode npm search query before building registry URL Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(ai-evals): add global-mode npm package search case Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
c8870d36ae | fix(sessions): session bar badge readouts, job persistence, refresh bounce (#10217) | ||
|
|
2b58df57fc |
feat(ai): extract prompt cache token usage from OpenAI and Azure providers (#10214)
* feat(ai): extract prompt cache token usage from OpenAI and Azure providers Parse the nested cache token details OpenAI returns and thread them into TokenUsage, matching the Anthropic and Bedrock providers. - sse.rs: add OpenAIPromptTokensDetails / OpenAIInputTokensDetails and the optional prompt_tokens_details / input_tokens_details fields. - other.rs (Chat Completions) and openai.rs (Responses): populate cache_read via .with_cache(cached_tokens, None). OpenAI's prompt_tokens/input_tokens already include cached tokens (cached is a subset), so total/prompt are unchanged; cache_read is recorded separately for reporting. For the same reason the frontend token-usage conversions are left as-is (adding cached would double-count); optional cache fields and a clarifying comment are added to prevent a future incorrect Anthropic-style fix. Fixes WIN-2207 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai): pin OpenAI/Azure cache-token deserialization paths Add regression tests deserializing the real Chat Completions and Responses usage payloads, guarding the prompt_tokens_details.cached_tokens / input_tokens_details.cached_tokens paths against a silent rename. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ai): extract to_token_usage() and test the cache mapping Address review nit: move the usage->TokenUsage conversion into OpenAIChatUsage::to_token_usage / OpenAIResponsesUsage::to_token_usage so the providers call one method and the tests exercise the real mapping. Tests now assert cache_read is populated while input/total are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0e04bc6991 |
fix: steer ai chat away from draft-blind api catalog reads and runs (#10202)
* fix: steer ai chat away from draft-blind api catalog reads and runs * fix: support api catalog tools in ai eval harness mock backend * fix: block remaining draft-blind read and list endpoints in api catalog * fix: scope eval fetch stub to handled benchmark api paths * feat: add deployed version read option to read_workspace_item * fix: include input schema in script workspace item reads * feat: add pagination to list_workspace_items * fix: paginate list_workspace_items per item type without cross-type loss * fix: window draft overlay in list_workspace_items by page and limit * refactor: simplify list draft overlay to capped page-1 merge * fix: label server-synthesized draft-only rows as drafts in listings * chore: fix stale eval comment and pin draft_only listing label |
||
|
|
f4308cf033 |
feat(ai): expose get_db_schema tool in global chat (#10207)
* feat(ai): expose get_db_schema tool in global chat * fix(ai): skip cross-workspace editor cache write in global get_db_schema * test(ai): add global eval case for get_db_schema resource lookup |
||
|
|
542a4842a3 |
feat(ai): live web-search source list on chat tool cards (#10210)
* feat(ai): live web-search source list on chat tool cards * fix(ai): restrict web-search source links to http(s), respect user collapse * fix(ai): read plural queries field for openai web-search labels |
||
|
|
b448af1da7 |
feat(sessions): live DOM access for the raw-app preview in AI sessions (#10129)
* feat(sessions): live DOM access for the raw-app preview in AI sessions Give the session chat read-only access to the rendered raw-app preview, plus inspector-picked element chips synced bidirectionally with the preview. - search_dom / read_dom tools: live, same-origin contentDocument reads by CSS selector (selector omitted = whole body), reusing the file engine over pretty-printed outerHTML (worker-guarded, bounded). Session-gated. - Inspector picks become app_dom_selector context chips (selector-only); the model fetches content on demand. Chips shown even in GLOBAL/session mode. - Multi-select chips synced with the preview: the chip list is the source of truth, pushed to the harness which renders one highlight per selector; add, chip-remove, and preview-× remove all stay in sync. Overlays stripped from search_dom output. The ui_builder harness changes (multi-select highlights + unique nth-of-type selectors) live in windmill-code-ui-builder and ship via the artifact re-pin. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(sessions): inline element prompt + inspector polish for raw-app DOM Session preview: - Add InlineElementPrompt: a floating mini-composer anchored top-left over a selected element; sends a chat turn scoped to that element (its chip is context). Autofocus + remount per selection, borderless input, label gutter so it clears the harness name+size pill. - autosize action gains an optional minHeight (default 30) so the compact inline input renders a tight single line. - Fix exiting select mode: Esc / inspector toggle-off now fully clear the session's DOM-selector chips (source of truth) so the overlays AND the inline prompt are dismissed together, instead of leaving them stranded. Full-page raw-app editor: - Add inspectorHoverOnly: the inspector highlights on hover (outline + name/size) but a click selects nothing — no persistent selection, no app-mode context pick, no inline prompt. Context badges: - ContextElementBadge gains a compact prop (passed only by AIChatMessage): the DOM-selector chip and tagged workspace items (app/script/flow) render smaller above a sent message, while every badge stays the same size in the composer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): address Codex review P1s on raw-app DOM context - Inline element prompt: queue the turn when the chat manager is already streaming (mirror the composer) instead of a concurrent sendRequest that would race the shared abortController / streaming buffers. - DOM selector chips now carry the raw-app path they were picked from. Each preview tab renders/pushes only its own app's chips, and a tab clears cross-app chips when it becomes the active DOM target — a selector could otherwise silently resolve against whichever raw-app tab is active (search_dom/read_dom target the active preview). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(raw-apps): re-pin ui_builder artifact to f79e1c3 Bumps the UI Builder artifact to the release built from ui_builder main after windmill-code-ui-builder#18 merged (the multi-select DOM inspector synced with the host chat). This is the artifact re-pin step that gates merging this PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): inline-prompt vertical centering + auto-collapse DOM tools - InlineElementPrompt: bias the textarea padding (pt-[5px] pb-[3px], same total) so the single line sits centered in the pill — the shared autosize floor otherwise adds slack at the bottom and the text read 1px high. - search_dom / read_dom: drop the explicit `autoCollapseDetails: false` so the tool card collapses after a successful read like other tools (it was copied from get_app_runtime_logs, where staying open is intentional). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): address Codex review on raw-app DOM access - [P1] Release the DOM-requester slot when the active raw-app tab is hidden. Preview tabs stay mounted, so switching to a flow/script/page (or another raw-app) tab left search_dom/read_dom targeting the now-hidden raw app. The slot is now claimed with a per-tab owner token and released via the effect cleanup; the runtime only lets the claiming tab clear it, so a set/release race between two raw-app tabs can't blank the new owner. - [P2] Strip the inspector outline classes from the clone ROOT (the selected element), not just its descendants — querySelectorAll skips the root, so a scoped read of the selected element leaked inspector-picked in its outerHTML. - [P2] Reword the AIChatDisplay context-chip comment to drop drafting-history narration ("now", "not as rows here") per AGENTS.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): scope a queued inline prompt to its own element (Codex P1) An inline element prompt submitted while a turn was streaming got queued as plain text; if the user selected another element before the queue flushed, the queued turn snapshotted the new selection and sent scoped to the wrong element (or none). The queue now carries a context snapshot: - queueMessage(text, context?) pins the selection present at submit time. - sendRequest accepts contextOverride and uses it verbatim as the turn's selected context (feeds both the optimistic bubble and the API message). - All three queue-flush sites thread the snapshot through and restore it if the auto-send bails. - With an override, only the queued message's own DOM chips are consumed from the live selection — a newer selection made since is left intact — preserving the one-shot chip semantics. onInlinePrompt snapshots getSelectedContext() when it queues. (Codex's other P1 — full-page-editor hover-only no longer attaching elements to App AI — is intentional per an explicit product decision, left unchanged.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(raw-apps): restore full-page editor App-AI inspector context Codex/Pi flagged a real regression: passing inspectorHoverOnly disabled the full-page editor's existing App-AI (app-mode) inspector context. Clicking an inspected element no longer set inspectorElement, so the app-mode SelectedContext badge and the element attach in prepareAppUserMessage were dead. inspectorHoverOnly and the session path (onInspectorSelect) are mutually exclusive — the flag was only ever set in the full-page/app-mode context, so it only suppressed the app-mode path. Remove it entirely (prop, guard, and the pass in +page.svelte), restoring the original click -> inspectorElement -> app-mode context behavior. Session behavior is unchanged; the hover highlight still works in both. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): snapshot inline-prompt context synchronously (Codex P1) The immediate inline-prompt path called sendRequest without a context override, so the selected element was read only after the async send preflight (attached-file refresh, beforeSend, global-skill refresh). Picking another element during that window attached the wrong one to the outgoing turn. onInlinePrompt now snapshots the selection synchronously at submit time and passes it as contextOverride on both the queued and immediate paths, so the prompt always rides with the element it was scoped to. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): drop a queued inline prompt's DOM chips when the app changes A queued inline prompt scoped to raw app A described A's elements, but the runtime's single DOM requester targets whichever preview is active. Switching to app B before the queue flushed left the turn describing A while search_dom / read_dom would query B. When a raw-app preview tab becomes active it now also strips the queued turn's DOM chips belonging to other apps (dropQueuedDomContextForOtherApps), so a queued prompt can't ask the model to read one app's selectors against another's live DOM. Non-DOM queued context is preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): scope inline prompt to its anchored element (Codex P1) The inline prompt sits over a single element but snapshotted EVERY selected DOM chip. With several elements selected (Shift-multi-select), a prompt shown over B sent both A and B as context, so "change this button" couldn't identify which one it meant. onInlinePrompt now uses its anchored `selector` argument (previously ignored) to keep only that element's DOM chip in the snapshot; non-DOM context is preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): preserve original context on edit/retry (Codex P1) restartGeneration resent with the live selection, so editing or retrying an element-scoped prompt lost its DOM selector (or adopted a newer one) — DOM chips are one-shot and cleared from the live selection after the first send. It now passes userMessage.contextElements as contextOverride, re-using the exact context the message was originally sent with. undefined (modes that don't attach contextElements) falls back to the live selection as before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(sessions): route search_dom/read_dom to the selector's own app preview The DOM requester was a single active-tab slot, so search_dom/read_dom always read whichever preview was visible. A turn scoped to app A that read the DOM after the user switched to app B would silently read B (Codex P1). Preview tabs stay MOUNTED when hidden, so app A's DOM is still there — the fix is to route by app instead of "active tab": - Every mounted RawAppEditorView registers its DOM requester keyed by its app path (runtime holds a map, not one slot); the visible tab is tracked separately as the default target. - search_dom / read_dom gain an `app_path` (surfaced per chip in the SELECTED DOM ELEMENTS block and the tool schema); the runtime routes the query to that app's still-mounted preview. No app_path → the active preview, or the only one open. - If the named app's preview has been closed, the tool returns "The preview for X is no longer open…" so the chat can explain it, rather than reading the wrong app. This makes the queued-context drop-on-switch (dropQueuedDomContextForOtherApps) redundant — removed. The lightweight submit-time snapshot (which element the message is about) is kept. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): address Codex review on DOM access (script strip + app-scoped chips) - search_dom/read_dom: strip the app's compiled <script> bundle from the whole-body clone so queries only see rendered HTML, not source (P1). - DOM chip add-dedup and removal now match both selector AND app path, so an identical selector in another app can't drop or block a chip (P2). - Refresh stale single-slot requester comments to describe per-app routing; reword the foreign-chip reset to reflect why it stays (composer chips are unlabeled by app) (P2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): scope DOM-chip clear to its app so a rebuild can't wipe another (Codex P2) Every mounted preview emits inspectorClear on rebuild, routed to onInspectorClearAll → clearSelectedDomElements(). Unscoped, a hidden app A rebuilding cleared app B's active chip and highlight. clearSelectedDomElements now takes an optional appPath; the preview-clear path passes its own path, while post-send and foreign-reset clears stay unscoped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): reject a <script>-root DOM query so the app bundle can't leak (Codex P2) The descendant strip (querySelectorAll('script')) skips the clone root, so a search_dom / read_dom query whose selector targets `script` serialized the whole compiled bundle. Reject a script root with an explanatory result before cloning. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): union queued inline-prompt DOM context + reject inspector-label root (Codex) - Multiple inline prompts queued during one stream accumulate their text, but queuedContext replaced (last wins) — dropping an earlier element's chip and misapplying its instruction. Union the DOM selector chips across queued prompts (non-DOM context still from the latest snapshot) (P1). - search_dom/read_dom: reject an .inspector-label clone root, like the <script> root — the descendant strip skips the root, so a `.inspector-label` query would return inspector chrome (P2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): restore consumed DOM chips when a turn is cancelled unsent (Codex P1) DOM selector chips are consumed from the live selection before the request goes out. If the user hits Stop before any usable output, restoreUnsentTurn put the text/pastes/images back but not the chips, so resending the restored prompt lost its element scope. Re-add the consumed chips on rollback (skipped on a queued- message handoff, which carries its own context). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): edit box edits the message's own context, sends what it shows (Codex P1) Editing a past message bound the edit box to the LIVE selection while the resend carried the message's original contextElements — so the box showed one set of chips (or none) but sent another, and add/remove in the box did nothing. The edit box now edits a copy seeded from the message's own contextElements, and restartGeneration sends that edited copy; a bare retry still falls back to the original. Drops the now-unused selectedContext prop from AIChatMessage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): keep DOM context coherent on dequeue/cancel + per-chip edit identity (Codex) - dequeueMessage dropped the queued draft's pinned DOM context, so returning it to the composer sent the live selection instead. Restore the draft's context on dequeue (P1). - Cancel-rollback re-added this turn's chips additively, mixing in chips selected mid-stream. Replace instead so the restored draft stays coherent — shared helper #restoreDomContext used by both paths (P1). - The context chip row keyed/removed by (type, title); repeated DOM elements share a title (two button.btn), so editing a multi-select message gave duplicate keys and deleting one chip removed both. Identify DOM chips by (appPath, selector) (P1). The queued-prompt instruction↔element association (Codex #1) is the accepted union tradeoff and is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): scope read_dom pagination + don't consume live chips on edit/retry Codex P1s: - read_dom's continuation note only renamed read_file, so following it with start_line alone dropped app_path/selector and re-read the active app's whole body instead of continuing inside the element. - contextOverride also carries an edit/retry's replayed copy of an older message's context. Consuming it from the live ContextManager stripped an identical chip the user had since selected in the composer, leaving their draft unscoped. Overrides now declare their origin ('pinned' vs 'replay'). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): keep DOM context paired with the draft it belongs to Restoring a draft's pinned DOM chips replaced the live selection unconditionally, but both restore paths can leave a different draft in the composer: - Cancel: an occupied composer declines the rolled-back text (restoreInstructions bails), yet the cancelled turn's chips still replaced the live ones — the draft the user typed during the stream kept its text but got retargeted. - Dequeue: queued text is prepended onto an existing draft, so both instructions share one composer; replacing the chips dropped the standing draft's element. restoreInstructions/prependText now report whether the composer took the text. Cancel restores context only when it did; dequeue keeps both drafts' chips when it merged into one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): scope an inline prompt's chip filter to its own app The filter narrowing a multi-select down to the anchored element matched on selector alone. Selectors are generated per app and collide across them, so an inline prompt in app B also carried app A's identically-named chip, leaving the model two indistinguishable referents. Since draft restoration can now legitimately hold chips from several apps, match (selector, appPath) — as the sibling deselect/clear handlers in this file already do. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4f1d636005 |
fall back to createFlow when the update target does not exist (#10206)
FlowBuilder's deploy branches solely on the `newFlow` prop. When an embedder
mounts the builder with `newFlow` false for a flow that was never deployed,
Deploy issues `PUT /flows/update/{initialPath}` and the backend answers
`Flow not found at name <path>`, so the first deploy can never land.
Confirm against `GET /flows/exists/{path}` before taking the update branch,
and create instead when nothing is deployed there. The create path and the
full-page editor (where `newFlow` is derived from `no_deployed`) are
unchanged.
Fixes WIN-2206
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
71f2d47cb4 |
feat: cap queued jobs per concurrency key on cloud (#10197)
* feat: cap queued jobs per concurrency key on cloud * fix: close preprocessed-flow bypass and bound concurrency cap scan * fix: only cap concurrency keys with an active concurrent_limit * chore: only load concurrency key cap setting when cloud hosted * fix: reject queued-job import on cloud |
||
|
|
1abfe49f7e |
stop the app editor crashing on a grid-less draft (#10203)
* fix(apps): default a missing grid so the app preview does not crash * fix(apps): migrate the adopted draft so the editor matches the viewer * docs: tighten grid-normalization comments to durable constraints * test: drop the migrateApp grid-default test |
||
|
|
83a354f831 |
feat: expose windmill api endpoint catalog to global ai chat (#10199)
* feat: expose windmill api endpoint catalog to global ai chat * fix: guard variable reads and deletes in ai chat api catalog * fix: clarify api catalog prompt example for run result access * fix: block deleteScriptByHash and document resource read boundary |
||
|
|
60a7a23a04 |
chore(main): release 1.763.0 (#10186)
* chore(main): release 1.763.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
89948bb441 | chore(git-sync): bump hub scripts to windmill-cli@1.762.3 (#10189) | ||
|
|
4bddefc518 |
wire topBar.path and editablePath customUi in FlowBuilder (#10187)
Mirror the ScriptBuilder whitelabel pattern in FlowBuilder so the `customUi.topBar.path` and `customUi.topBar.editablePath` options actually take effect: - add `editablePath?: boolean` to `FlowBuilderWhitelabelCustomUi.topBar` - gate the EditorHeader block on `customUi?.topBar?.path != false` and pass `pathEditable`/`summaryEditable` (the latter wires the pre-existing but unused `editableSummary` flag) - hide the Path field in FlowSettings when `topBar.editablePath` is false Defaults are unchanged (`!= false`), so non-whitelabel flows still show the path header and settings field. Fixes WIN-2200 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
42da20ae97 |
feat(ai): gate data pipelines in sessions behind a dev flag (#10178)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d5b0a1b5c7 |
chore(main): release 1.762.2 (#10184)
* chore(main): release 1.762.2 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
252ffd0401 |
chore(main): release 1.762.1 (#10181)
* chore(main): release 1.762.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
3ffce7e2cc |
chore(main): release 1.762.0 (#10175)
* chore(main): release 1.762.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
8828341a2b |
fix(frontend): scope session pipeline trigger editors to the session workspace (#10032)
* fix(frontend): scope session pipeline trigger editors to the session workspace Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): hoist triggerWorkspace decl above GCP init-time getBaseUrl call Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): scope nested trigger pickers to the session workspace The triggerWorkspace resolver scoped direct trigger CRUD calls to the session's (forked) workspace, but nested pickers still defaulted to the nav `$workspaceStore`: in a fork session, resource lists/creation, variable creation, and path-existence checks ran against the parent workspace while save/delete targeted the fork — misleading options and false path-validation failures. Thread `wsId` into the nested controls of the 9 pipeline-canvas kinds: - `<Path workspaceOverride={wsId}>` (8 editors) — path + folder checks - `<ResourcePicker workspace={wsId}>` (6 config sections; add the resolver to MqttEditorConfigSection, which lacked `wsId`) - SQS `<VariableEditor workspace={wsId}>` — variable creation Also drop the per-site `wsId` rationale comment repeated across ~20 files; the invariant is documented once in triggerWorkspace.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): scope trigger runnable picker to the session workspace The runnable picker (`ScriptPicker`, reached via `TriggerRunnablePicker` and directly in the schedule editor) listed scripts/flows/apps from the nav `$workspaceStore` with no override, so a forked session offered the parent workspace's runnables when attaching a script/flow to a trigger. Add an optional `workspace` prop to `ScriptPicker` (defaults to `$workspaceStore` → no change for existing callers), pass it through `TriggerRunnablePicker`, and wire `wsId` from the 7 trigger editors that use it plus the schedule editor's 3 direct pickers. Completes the nested-picker workspace scoping; the excluded kinds (azure/http/websocket/native) keep their own ScriptPickers unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): scope trigger error-handler, folder default, and runnable actions Follow-up to the trigger-workspace scoping: three subtrees still read the nav `$workspaceStore` in a forked session. - ErrorOrRecoveryHandler (via TriggerRetriesAndErrorHandler in the 7 in-scope editors): add a `workspace` prop (defaults to `$workspaceStore`) and route handler lookup/schema, Slack/Teams settings, test jobs, and run links through it, so the error handler is resolved/tested/saved in the session workspace instead of A while the trigger lives in B. - useFolderDefaultPermissionedAs: accept an optional workspace getter so a `f/...` trigger's default permissioned-as is read from the session workspace, not the nav one (PermissionedAsLine passes `() => wsId`). - ScriptPicker actions: scope the View drawer (`getScriptByPath`) and `FlowPathViewer` to `effectiveWorkspace`, and carry `?workspace=` onto the Edit/View routes when an explicit override is set (the layout consumes the param, same mechanism as editInFork). The param is only appended when a workspace override is passed, so existing callers' links are unchanged. Also consolidate the repeated workspace-scoping comment in PipelineTriggerEditors (the invariant lives in triggerWorkspace.ts). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): scope error-handler/schedule nested consumers to the session workspace Address CI Codex review on #10032 — the error-handler and schedule subtrees still had nested consumers reading the nav workspace: - ErrorOrRecoveryHandler: pass the resolved workspace to its own nested `ScriptPicker` (custom-handler list + View/Edit) and add a `workspace` prop to `ChannelSelector` (Teams channel listing); carry the acting workspace onto the "create from template" link. - ScheduleEditorInner: pass `workspace={wsId}` to the error/recovery/ success `ErrorOrRecoveryHandler` panels, `workspaceId={wsId}` to `WorkerTagPicker`, and the workspace query param onto the dynamic-skip template link. Template-link and picker overrides only diverge from `$workspaceStore` when a session override is set, so non-session callers are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7a139ab23e |
feat(ai-chat): image attachments and agent raw-app screenshots (#10130)
* feat(ai-chat): add image attachments and agent raw-app screenshots Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(ai-chat): generalise take_screenshot fidelity caveat Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): keep compaction boundary on a displayed user message Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(raw-apps): count line boxes by vertical overlap, not rect count Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): enforce vision gating and bound image attachments Refuse images on known text-only models instead of warning and sending them anyway; cap input bytes before decode; keep clipboard text when it accompanies a bitmap; don't queue a message whose images can't ride the plain-text queue. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * perf(ai-chat): trim take_screenshot schema and shrink the card's copy Move the fidelity caveat from the tool def onto the tool result: the def is re-sent every global iteration (~258 tok), while the caveat only matters once a capture exists. Keep a downscaled copy in displayMessages when it is actually smaller — those are never compacted and are re-cloned on every saveChat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): carry attached images through the message queue Enter during a streaming turn queued the text and silently dropped the images, so the auto-send was not the message the user submitted. The queue now holds both, moved together via takeQueue/clearQueue/restoreQueue so none of the three flush sites, the dequeue-to-composer path, or the two conversation-switch drops can leak one without the other. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): gate screenshots on vision, narrow when the tool fires take_screenshot buffered an image unconditionally, so a text-only model got an image_url and rejected the turn; the attach-time check never covered it, nor a model switched after attaching. Gate before capture and again at send. Only reach for the tool when the user raises how the app looks, rather than after every UI edit. A collapsed preview keeps the iframe mounted at zero width, passing the ready checks and then failing inside the rasteriser as '[object Event]'. Name it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): hold sending while attachments decode addImages read the free-slot count before its await and appended after it, so a send during the ~50-800ms decode cleared images while the closure still wrote to them, landing the picture on the following message; two drops also claimed the same slots and could pass the cap. Reserve slots up front, block sending until they resolve, and show a placeholder so the held send is explained. Keep only a bounded copy in the transcript: displayMessages are never compacted and are re-cloned on every save. Measured 6.1x smaller per attachment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): route screenshots to the visible tab, resend full-res on retry Every mounted raw-app editor claimed the runtime's single screenshot slot, so take_screenshot could capture a background tab's app; ownership now follows the visible tab and only the owner releases it. restartGeneration resent displayMessages' images, which became a 384px thumbnail when the transcript copy was bounded — retries downgraded the model's own input. Recover the sent parts from the API message instead. Move modelSupportsVision to modelConfig: it was untestable behind lib.ts's monaco import chain, and the denylist missed bundled text-only defaults (Groq/Together Llama 3.3, Foundry Phi-4 and Mistral-Large). Llama 3.2 and Phi-4 split by variant, so both are matched narrowly. Pinned against the shipped defaultModels. Decode attachments one at a time and derive the preview from the bounded copy: a 12MP bitmap is ~48MB and the batch was held live at once, decoded twice each. The attach tooltip claimed nothing is uploaded, which is untrue for images. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): keep images out of text-only turns and bound the queue The vision gate only dropped the current turn's images, so history's image parts still went out after a switch to a text-only model and failed the request; strip the outbound copy instead, leaving history intact for a switch back. queueMessage had no cap, and each queued send clears the composer for another eight, so repeated sends stacked an unbounded batch into one message. Editing a message resent displayMessages' bounded copy, downgrading the model's own input; retries recovered the full-size one but then re-persisted it at full resolution. storedImages pairs the API message with its transcript entry so both paths resend the original and re-persist the bounded copy. Reserve image slots before awaiting text attachments: the gap left sending enabled with an image pending, measured ~90ms for a 40-file drop, now ~8ms regardless of batch size. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): treat deepseek-v4 as text-only deepseek-v4-pro ships as a bundled default and the gate let images through to it, so an attachment would fail the turn. DeepSeek's vision line is deepseek-vl. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): drop a rejected image instead of wedging the conversation A provider that refuses an image leaves it in history, so every later turn resends it and fails identically: the chat is stuck until the user edits the message or starts over, and Retry re-sends the same image. The vision gate only knows the models we ship, so this is the net for the rest. Strip the parts on an image-related rejection and say so; unrelated failures keep the image. Verified at the wire that no provider rejects a base64 data URL: anthropic (source.base64), openai/gpt-4o (input_image), googleai and aws_bedrock/claude (image_url passthrough) all 200 and read the image. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): match text-only models exactly, from provider API docs The gate guessed by substring over model names, which answers the wrong question. What matters is whether a provider's API accepts image parts, not whether the model can see: DeepSeek V4 ships vision in its chat product that its API has no content type for, and o3-mini gained vision in ChatGPT the API never exposed. Neither is inferable from a name. Substrings also block working models. 'mistral-large' matches Mistral Large 3, which takes images; 'phi-4' matches Phi-4-multimodal, which does too. A wrong entry blocks with no override, while a missing one costs a turn and recovers via the rejection path, so the list is now exact ids only, each backed by a provider doc. Verdicts verified against provider API docs rather than recall. Live-checked where a doc was contradicted: Bedrock's compatibility matrix claims no Anthropic model is served over chat completions, but it serves images fine. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): stop retry resurrecting a rejected image The rejection fallback strips the image from history but leaves the bubble's thumbnail so the user can still see what they sent. storedImages fell back to that thumbnail when the API message had no parts, so Retry re-attached the very image the provider had just refused and failed identically — the conversation stayed wedged through the one control offered to escape it. Found by retrying in the UI; unit tests, wire tests and four review passes all missed it, since it only exists between two separate fixes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): harden image rejection recovery and drop-path attachment * fix(ai-chat): fix image drop race, mid-turn vision gate, retry aliasing * fix(ai-chat): key vision denylist by provider, flatten alpha before jpeg * feat(ai-chat): offer take_screenshot on chromium only, ask for one elsewhere * feat(ai-chat): image-only sends and click-to-expand image previews * fix(ai-chat): capture screenshots at 2x and expand tool images full-res * feat(frontend): expandable image previews in composer and result views * fix(ai-chat): image-only send edge cases from review round * fix(ai-chat): keep image-only drafts on rollback, track failing model id * fix(ai-chat): gate rejection recovery on the failing iteration's model * refactor(ai-chat): record iteration model via onBeforeIteration, trim tests Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): restore composer draft when beforeSend preflight fails Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): bound cumulative outbound image bytes per request Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): make the image byte bound part-granular so over-cap turns keep a subset Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): evict newest-first within a message in the image byte bound Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): prune over-cap images from stored history, not just requests Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): bound history at every save boundary, keep thumbnail pairing across eviction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): slot-align storedImages so the bubble expands the right image after eviction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): match rejection keywords as whole words so provisioning errors keep images Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): match input_image rejections, restore images refused by non-GLOBAL modes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): defer non-GLOBAL image refusal restore past the composer clear Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): persist full tool screenshots for post-reload expansion Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(ai-chat): persist chat images out-of-band via blob-store refs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): scope image blobs per chat and stop cap-eviction rotation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): keep blob-cap chronology across drop-oldest compaction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(ai-chat): derive blob eviction from the saved record, not write times Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): serialize chat history DB writes per manager Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): pin queued history writes to the enqueue-time user database Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): delete stale image blobs only after the chat record commits Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): don't double-restore a queued image-only draft on vision refusal Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): label image-only chats and evicted image-only bubbles Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): keep the in-memory chat mirror hydrated for DB-less sessions Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): converge the chat mirror to refs after a successful DB commit Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): guard mirror convergence against rewinding newer saves Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): invalidate pending convergences on identity re-init, keep retry image names Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): bound the screenshot raster before rasterization Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(ai-chat): drop the no-IndexedDB in-memory image fallback Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
d0aa7dca13 |
fix: parse all names in grouped go param declarations (#10165)
* fix: parse all names in grouped go param declarations Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump windmill-parser-wasm-go to 1.761.0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a9fc9f74b2 |
fix(ai): unbreak session chat compaction for Anthropic models (#10171)
* fix(ai): cap chat compaction summary output so Anthropic non-streaming calls succeed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: pin the compaction summarizer's maxTokensCap Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): cap testKey completion so Anthropic key tests pass the SDK pre-flight Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
7d2c5ceb0f |
fix(flows): make updateFlow body path optional so AI can update flows (#10176)
* fix(mcp): default a body field to its same-named path param so updateFlow works Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: trim mcp path-param fallback helper comment Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(mcp): keep path params un-mangled so update tools take plain `path` Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(flows): default update_flow body path from URL via EditFlow Harmonizes updateFlow with the EditVariable/EditResource/EditApp convention: the flow to update is identified by the URL, so the body path is optional and only needed to rename. Fixes the 422 at the API layer for every client (MCP, the in-app AI chat, raw HTTP), not just the MCP tool schema. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(mcp): drop redundant body-path fallback now that the server defaults it Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: fix stale generator comment after removing mcp body-path fallback Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(flows): mark updateFlow body path optional in the openapi contract Adds an `EditFlow` schema (path optional) for the update route so the public contract matches the server; createFlow keeps `OpenFlowWPath` (path required). Also trims two test comments to record constraints rather than history. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |