mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-08 16:03:27 +00:00
ce6e2f7ade25ca91c375f0de5f1be3f1c82ccb47
13424
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ce6e2f7ade | fix(frontend): stop echoing draft values in global chat write tool results (#9530) | ||
|
|
ad37eab82b |
copy folder labels on workspace fork, normalize cleared labels to NULL (#9529)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
765f50c474 |
feat: folder-level label inheritance for scripts, flows and jobs (#9524)
* feat: folder-level label inheritance for scripts, flows and jobs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: use SECURITY DEFINER folder_labels() for RLS-consistent inheritance Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: extend folder label inheritance to apps, resources, variables, schedules Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dc60e1aa17 |
fix(cli): include lock-relevant script content in lock cache key (#9528)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4e868062d4 |
fix(frontend): improve AI chat markdown and typing dots in dark mode (#9497)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ab015dd44b |
style: lean tool-call rendering and tighter ai chat spacing (#9517)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7f987e8c98 |
feat: add reasoning effort control and thinking display to AI chat (#9511)
* feat: add reasoning effort control and thinking display to AI chat Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: strip legacy /thinking suffix from configured model slots Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: refine reasoning effort UX and drop dynamic-capability scaffolding Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: show textless reasoning on the typing indicator Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
61a4dd6e91 | refactor: extract frontend uuid helper (#9521) | ||
|
|
afddfe8445 |
feat(worker): #ssh directive to run a bash script on a remote SSH host (#9479)
* feat(worker): #ssh directive to run a bash script on a remote SSH host Add a first-class `#ssh <resource_path>` bash directive that reroutes a normal bash script to run on a remote host reached over SSH (a jump/utility node) instead of on the worker, with full parity: typed positional args in, structured result out, live streamed logs, cancellation, and remote exit-code propagation. It mirrors the existing `# sandbox <image>` precedent: the directive is parsed in handle_bash_job and reroutes to a specialized handler that reuses handle_child for all execution plumbing. - windmill-common: BashAnnotations::ssh_target() parser (+ unit test) and the ssh_execution_enabled instance setting (off by default) - windmill-worker: reroute hook in bash_executor + ssh_executor_oss shim. OSS returns a clear "enterprise feature" error; the real handler lives in ssh_executor_ee.rs (private feature) and is gated by a valid enterprise license + the instance setting. - examples/usecase/ssh-execution-wrapper: the ssh_target resource type, a userland wrapper (no-license fallback), and a README documenting both paths and the trade-offs vs agent workers. EE companion: windmill-labs/windmill-ee-private (ee-repo-ref.txt bumped). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(worker): ssh host-key opt-in, 0600 key write, instance setting UI * chore: update ee-repo-ref * feat(worker): #ssh $arg form to take the ssh target from a job argument * fix(worker): #ssh token must look like a target; $arg restricted to path strings * fix(worker): tighten #ssh parser to exact directive; add -- ssh destination guard * chore: update ee-repo-ref to d45b9a6cbe40f7fe5d322c850c50f64a6980e4f0 This commit updates the EE repository reference after PR #609 was merged in windmill-ee-private. Previous ee-repo-ref: 2804f1aa8e74b3a7733aeb6f5044d5085193872a New ee-repo-ref: d45b9a6cbe40f7fe5d322c850c50f64a6980e4f0 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> |
||
|
|
365e20410e |
feat(ai-chat): collapse big pastes, cap input height, escape HTML (#9487)
* feat(ai-chat): collapse big pastes, cap input height, escape HTML Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(ai-chat): concentrate paste expand/render in a ChatDraft module Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(ai-chat): step caret over paste chips as one unit Arrow-Left/Right now jump edge-to-edge across a collapsed-paste chip instead of crawling through the invisible token characters, and snap the caret out if it lands inside a token. Shift extends the selection across the whole chip; word/line jumps (alt/cmd/ctrl) are left to the browser. Mirrors the existing atomic-deletion behavior so traversal and deletion both treat the chip as a single object. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): atomic chip deletion on overlapping selections + plural label A selection that partially overlapped a paste token previously hit the collapsed-caret early-return, so the browser deleted a partial token and left an orphaned zero-width run plus a dangling pastes registry entry. handlePasteDeletion now widens the deletion range to cover each overlapped token whole and drops all affected pastes entries (shared removePasteRange helper). Strict overlap, so abutting a chip edge doesn't pull it in. Also route line-count pluralization through a shared lineCount() helper so a 1-line paste reads "1 line" in the conversation bubble too, not "1 lines". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): full chip atomicity via beforeinput + expand on copy/cut Addresses review nits on PR #9487: - Chip atomicity is no longer limited to Backspace/Delete keydown. A new beforeinput handler takes over any selection-spanning edit that overlaps a paste chip — typing over a selection, paste, drag-and-drop, and Backspace/Delete over a selection — and applies it to the whole token(s) via a shared replacePasteRange, so a partial edit can no longer leave an orphaned zero-width run or a dangling pastes registry entry. handlePaste is likewise widened so a large paste onto a chip replaces it whole. The keydown handler now only covers the collapsed-caret-at-boundary case beforeinput can't see. - Copy/cut of a selection containing a chip now puts the expanded content on the clipboard instead of the chip label + its zero-width run; cut also removes the chip whole. This also removes the duplicate-token re-paste vector (the clipboard never carries a raw token anymore). - Rename lineCount -> lineCountLabel: it returns a formatted string, not a count, so the name shouldn't read like an accessor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): address cubic + claude review findings on the paste feature cubic-dev-ai findings: - autosize: clear inline overflow-y on the uncapped path so a capped-> uncapped toggle can't leave a stale `auto`/`hidden`. - pasteTokens: new countLines() ignores a single trailing newline, so a 10-line paste with a trailing \n no longer counts as 11 (off-by-one in shouldCollapsePaste and the chip's reported line count). - ContextTextarea: the @-mention picker anchor now subtracts the textarea's own scrollTop/Left and is recomputed on internal scroll, so it stays pinned once the input is capped at 40vh and scrolls. claude re-review findings: - ContextTextarea: drag-and-drop of a selection containing a chip now puts the expanded content on the drag payload via ondragstart (mirroring copy/cut), so a dragged chip no longer orphans its token and loses the pasted content (or leaks the label to an external target). - ContextTextarea: handlePasteBeforeInput now guards on e.cancelable and a HANDLED_INPUT_TYPES whitelist, so historyUndo/Redo (Ctrl+Z) and non-cancelable insertCompositionText (IME) are left to the browser instead of being reinterpreted as a delete / rewritten mid-composition. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
fddabe9c5c |
feat: clear conflict error + force delete when reusing a fork workspace id (#9499)
* feat: clear conflict error + force delete when reusing a fork workspace id Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: guard fork force-delete against double submit Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b894f783f1 |
feat(ai-chat): quick access to AI prompt settings from chat (#9508)
* feat(ai-chat): quick access to AI prompt settings from chat Add a cog next to the AI model selector in the chat that opens a dropdown to edit the current chat mode's user (localStorage) and workspace (DB ai_config) AI prompts, reusing the shared AIPromptsModal. Workspace prompt editing is admin-only; non-admins get a read-only view with an info Alert. The dropdown and modal footer include an admin-only deep-link to the full AI settings page, opening in a new tab. Workspace save re-applies the saved custom_prompts onto the store after the ai_config round-trip, since effective_ai_config falls back to the instance config (and would drop them) when the workspace has no providers of its own. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): snapshot mode when opening prompt settings modal Prompt edits were keyed off the live chat mode (a $derived of aiChatManager.mode). If the chat mode changed while the modal was open, save/reset/hasChanges and the modal's target mode would follow the new mode and write to the wrong key. Capture the mode into an activeMode snapshot at open time and use it throughout the modal lifecycle. Identified by cubic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): base-prefix AI settings links and reset hasChanges after save - Prefix the AI-settings deep-links (dropdown item + modal footer) with {base} so they resolve under a deployment base path instead of 404ing. - After a successful save, sync customPrompts to the trimmed value so hasChanges flips back to false (Save/Reset disable, Reset no longer snaps the textarea). Identified by Claude review. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): gate workspace prompt editing on workspace having own providers When a workspace has no AI providers of its own (it uses instance defaults), the backend never makes workspace custom_prompts effective — get_copilot_info returns the instance config verbatim. A workspace prompt saved in that state would be dead config that silently disappears on reload, and the earlier re-apply hack also transiently replaced instance prompts in copilotInfo. Mirror the settings page (AISettings.svelte): detect the workspace's own providers in the same getSettings fetch used to seed the prompt, and when there are none, surface the workspace prompt read-only with an explanatory Alert (new readOnlyReason prop on AIPromptsModal) instead of an editable field. Since editing is now gated to workspaces with their own providers, effective_ai_config equals the saved config, so the re-apply is reverted to a plain setCopilotInfo(effective). Identified by Claude review. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): await async save before closing prompts modal AIPromptsModal.handleSave called onSave() without awaiting, then closed the modal immediately — so the workspace save round-trip was still in flight when the modal dismissed, with no loading state and any error toast landing after the modal was gone. Make handleSave await onSave (now typed to allow a Promise), show a loading state on Save while in flight, and keep the modal open if the save throws. AIChatSettingsMenu.save() re-throws on failure so the modal stays open. The synchronous user-prompt path and existing callers are unaffected. Identified by Pi review. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0b178437ce |
fix(frontend): clarify trigger filters match the message parsed as JSON (#9516)
* fix(frontend): clarify trigger filters evaluate decoded JSON, not base64 The filter description in TriggerFilters didn't explain that filters are evaluated on the original decoded JSON payload, while a base64-encoding trigger (Kafka) still delivers the payload to the runnable as a base64 string. Users saw base64 in their scripts and assumed filters couldn't reference JSON keys. Adds a `payloadBase64Encoded` prop (set by the Kafka editor) that appends a sentence clarifying the runnable receives base64 while filter keys reference the original JSON structure. WebSocket triggers deliver the raw message string, so the base64 caveat is intentionally not shown there. Fixes WIN-2029 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): reword trigger filter help to "parsed as JSON", not "decoded" Nothing is base64-decoded during filtering: WebSocket parses the text frame directly, Kafka converts the message bytes to UTF-8 then parses. The base64 form only exists on the delivery path to the runnable (Kafka, V2). Reword to "filters match against the message parsed as JSON" and, for base64 triggers, clarify filters run on the message before encoding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): correct filter-key example to match backend semantics Review follow-up: the "data.status" example implied dot-path filter keys, but the backend (SupersetVisitor in filter.rs) matches keys literally against top-level JSON fields — "data.status" would never match a nested object. Reword to state keys match top-level fields and nested matching is done via an object value (key data, value {"status": "active"}). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3c3f15722f |
feat: prefer idle worker pods on k8s autoscaling scale-in via pod-deletion-cost (#9515)
* docs: investigate pod-deletion-cost for k8s autoscaling scale-in (WIN-2028) * docs: correct worker_instance aggregation claim per review * docs: drop investigation doc in favor of implementation * chore: bump ee-repo-ref for pod-deletion-cost autoscaling scale-in * chore: update ee-repo-ref to 80c39bf7f3bfeda4cf0974ce32826f53affd9574 This commit updates the EE repository reference after PR #610 was merged in windmill-ee-private. Previous ee-repo-ref: 51f79d4a49dd6491f4809f3edcb3919571719da7 New ee-repo-ref: 80c39bf7f3bfeda4cf0974ce32826f53affd9574 Automated by sync-ee-ref workflow. --------- Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
3119e16ed8 |
feat: prompt browser confirmation on page exit with unsaved changes (#9503)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
676e907ea6 |
docs: replace dead 0x0.st with gh-based PR screenshot recipe (#9512)
* docs: replace dead 0x0.st with gh-based PR screenshot recipe Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: harden PR screenshot recipe (filename, secrets, CI fallback) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1d4328877f |
fix: make default chat model optional in AI settings (#9514)
* fix: make default chat model optional in AI settings Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: stop auto-seeding default chat model on provider enable Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a6a5600833 |
feat(flow): support worker tag override on AI agent steps (#9513)
* feat(flow): support worker tag override on AI agent steps * refactor: drop ineffective tag passthrough in nested agent tool path * chore: regenerate openflow-derived system prompt artifacts |
||
|
|
cf9ad54181 |
feat: workspace protection rule to restrict anonymous app deployment (#9509)
* feat: restrict anonymous app execution mode to admins Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: add regression test for anonymous app admin gate Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: gate anonymous app mode behind workspace protection rule Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: lock app row on anonymous-mode check, fail closed while rules load Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
7fc5340da3 |
fix(frontend): allow copy/paste shortcuts inside ConfirmationModal (#9505)
The modal's window keydown-capture handler called preventDefault() and stopPropagation() on every keystroke while open, which swallowed Cmd/Ctrl+C and Cmd/Ctrl+V (and blocked typing in any child input). Only intercept Enter/Escape without modifiers and let all other keys through. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
08da7a121b | fix(nsjail): make ansible collections mount non-mandatory (#9510) | ||
|
|
dc368a9669 | fix(nsjail): make ansible uv tools mount non-mandatory (#9507) | ||
|
|
53da8ac148 |
docs(cli): add Windmill documentation section to generated AGENTS.cli.md (#9506)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c80c6d8fcd |
fix(frontend): enable Apply button when env vars change in worker group config (#9501)
Adding or editing environment variables in the worker group config drawer did not enable the "Apply changes" button. `hasChanges` compares the original `config` prop against the local `nconfig` copy, but env var edits only mutate the local `customEnvVars` array — they aren't synced into `nconfig.env_vars_static`/`env_vars_allowlist` until the Apply handler runs. This left the button stuck disabled despite real changes. Add a `hasEnvVarChanges` derived that reconstructs the original env vars from `config` and compares them to the current `customEnvVars`, and include it in the Apply button's disabled condition. Fixes WIN-2023 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
49561f9e49 | update claude actions to fable 5 | ||
|
|
cfe5119035 |
feat(ai): add list_runs and get_job_logs tools to global chat mode (#9488)
* feat(ai): add list_runs and get_job_logs tools to global chat mode Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ai): always suppress ansi hint in get_job_logs, drop misnamed param Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai_evals): add global list_runs and get_job_logs eval cases Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style(ai): trim get_job_logs description and format global core Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai): surface list_runs/get_job_logs output as tool result The tools set showDetails but never set message.result, so the details panel rendered "No result yet" even on success. Set result in setToolStatus (logs go in result for get_job_logs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4c22e3b712 |
fix: inherit container NO_PROXY into MITM tracing proxy job exclusions (#9492)
When HTTP request tracing is enabled, the NO_PROXY injected into traced jobs was built solely from the no_proxy_hosts instance setting, ignoring the worker container's own NO_PROXY. Enabling tracing therefore silently dropped every exclusion an operator had already configured at the container level, funneling those hosts into the MITM proxy (and on to any upstream corporate proxy). The upstream-relay side already honored the container NO_PROXY; this makes the injected-into-jobs side symmetric by merging both sources. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
136c88a231 |
docs(skills): decouple safe local commands from destructive sync push (#9467)
* docs(skills): decouple safe local commands from destructive sync push The schedules, triggers, and resources skill templates lumped every CLI command under a blunt "do NOT run them yourself" directive. This conflated two very different risk profiles and forbade the agent from running even read-only/local commands, creating needless friction. Align these three with the nuanced policy flow-cli.md already uses: keep `wmill sync push` defensive (it deploys and can be destructive to remote state — only run when the user explicitly asks to deploy/publish/push), while letting read-only commands (`sync pull`, `schedule`, `resource list`) be run freely. Regenerated auto-generated skills + skills.gen.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(cli): warn that sync push is destructive in dry-run output Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(skills): clarify sync pull mutates local files, not read-only 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: centdix <farhadg110@gmail.com> |
||
|
|
66c0334e70 |
chore(main): release 1.721.0 (#9480)
* chore(main): release 1.721.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.721.0 |
||
|
|
c258928ab6 |
fix(cli): reconcile case-only path drift during sync on case-insensitive filesystems (WIN-2020) (#9485)
* fix(cli): reconcile case-only path drift during sync on case-insensitive filesystems Windmill paths are case-sensitive, but Windows (and the default macOS setup) use case-insensitive filesystems. The real-world failure behind WIN-2020 is not a user authoring both f/Caps and f/caps — it is a single capitalized folder whose on-disk casing silently drifts (Windows stores and reports whatever case the directory was first created with, regardless of the server's path). The diff then sees the drifted local path as a brand-new item and emits a destructive "delete f/Caps + add f/caps" pair, so a capitalized folder appears to vanish and a lowercase clone shows up out of nowhere — and a push can clobber the real server item. Fix: on a case-insensitive filesystem, reconcile case-only drift before diffing. The server's path casing is authoritative, so compareDynFSElement now rewrites local keys that differ from a remote key only by case to the server's casing (canonicalizeCaseInsensitiveKeys), making the diff treat them as the same item. Case-insensitivity is auto-detected by probing the sync directory, with a WMILL_CASE_INSENSITIVE_FS=true/false override to force Windows behaviour (or emulate it for tests / cross-platform repos) on any host. Reconciled paths are summarized in a single info line. Genuinely unrepresentable collisions — two DISTINCT server paths that differ only by case — cannot be canonicalized to one target; those are detected and warned about on every platform so a case-sensitive-Linux author learns their tree won't round-trip for a Windows/macOS teammate. Tests: - Pure unit tests for findCaseInsensitiveCollisions, canonicalizeCaseInsensitiveKeys and summarizeCaseRewrites (platform independent). - An end-to-end drift test that runs on BOTH CI jobs: on the Windows runner it exercises the real case-insensitive NTFS + auto-probe; on Linux it reproduces the drift via rename, asserts the destructive phantom appears without the fix, and asserts a clean no-op push with the fix forced on. Fixes WIN-2020 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): canonicalize local-only descendants of drifted folders; dedupe nested case collisions Address two review findings on the WIN-2020 case-insensitive sync fix: P1 (correctness): canonicalizeCaseInsensitiveKeys previously only rewrote local keys with an exact full-path remote match. A brand-new local file under a drifted folder (e.g. adding f/caps/New.ts when the server has f/Caps but no f/caps/New.ts) had no exact match, so it kept its lowercase casing and push uploaded it as-is — recreating f/caps beside f/Caps and reintroducing the very collision the fix prevents. Canonicalization is now segment-by-segment against a trie of remote paths, so local-only descendants inherit the longest unambiguous server folder casing. A segment is only adopted when the server casing is unambiguous; at the first ambiguous/unknown segment the remainder keeps local casing. The original key's separator style is preserved so rewritten keys still round-trip. P2 (nit): findCaseInsensitiveCollisions reported the folder group AND a nested per-file group when case-variant folders held same-named files, inflating the "Found N path(s)" count. It now reports only the shallowest clash (drops a group whose ancestor prefix is itself a collision). Tests: add unit coverage for the new-file-under-drifted-folder rewrite, the stop-at-first-unguided-segment behavior, and shallowest-only collision reporting; extend the e2e drift test to assert a new item added under the drifted folder is pushed under the server's folder casing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
92c21bbe65 |
fix: drop archived items from fork compare (spurious 'not visible' warning) (#9481)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5f41ddd3a5 |
fix: require auth to view approval details when user_auth_required (#9482)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b0b330c786 |
feat: deployed↔draft compare + AI-session draft bar (#9435)
* feat: deployed↔draft compare for current workspace + session draft bar Add a "Deployed ↔ draft" comparison alongside the existing fork-vs-parent compare flow, and surface drafts in the AI session UI. - Merge the fork-direction toggle (Deploy to parent / Update current) and the new deployed↔draft mode into one 3-way CompareModeToggle, rendered inside the comparison card. Hidden in non-fork workspaces (draft only). - CompareDrafts: list/deploy/discard server drafts (scripts, flows, apps incl. raw apps) via shared WorkspaceDeployLayout. - Session draft bar (SessionDraftBar) mirrors the fork bar, only visible when drafts exist; its diff button opens the shared read-only diff drawer extracted as WorkspaceDiffDrawer (ForkDiffDrawer + DraftDiffDrawer are thin wrappers over it). - WorkspaceDraftsBanner: home banner linking to draft review. - Backend: GET /drafts/count endpoint for the draft-count badge. - Raw app draft deploy (rawAppDeploy.ts) + vite /ui_builder proxy headers so the bundler iframe loads cross-origin. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: refine draft/fork compare toggle UX Follow-up polish on the merged deploy/draft compare control: - Relabel the draft toggle to "Deploy draft (N)" and show per-direction counts on all three toggle buttons (deployable / updateable / drafts), suppressed when zero. Counts are computed page-side so they persist in draft mode too. - Warn before deploying to the parent when the fork has undeployed drafts ("Only deployed versions in this fork can be sent to {parent} …") with a one-click link to the draft view; milder note in the update direction. - Show an empty-state message per direction ("Nothing to update — this fork is up to date with {parent}") instead of a table of greyed, non-actionable rows; hide the deploy/update button in that case. - Drop the standalone "Pending drafts" info alert from the draft list. - Align the fork "Show diff" button to the non-deprecated Button API (unifiedSize, onClick, startIcon) so it matches the draft one; mark "Discard draft" destructive. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: link compare row titles to the item editor - Render each compare row title (fork and draft) as a link that opens the item in a new tab, scoped to the current workspace (raw apps route to /apps_raw/edit), matching the AI-session diff drawer: target=_blank, hover underline + ExternalLink icon, click stops row-selection propagation. Kinds without an editor stay plain; the fork rename markup is preserved. - Drop the "Kind → name" prefix from draft rows — that arrow reads as the rename visual and the kind is already shown by the row icon. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: clickable rows + multi-select in deploy layout - Make deploy-layout row cards selectable on click via an opt-in `selectOnRowClick` prop on the shared Row (default off, other tables unaffected); clicks on the checkbox, title link and action buttons are ignored. Adds role/tabindex + Enter/Space keyboard support. - Support multi-select with modifier keys like classic list pickers: Shift+click selects the contiguous range from the anchor row; Cmd/Ctrl (and plain) click toggles a single row. select-none avoids text highlighting on shift-click. - Turn the "Select all" text into a <label> associated with its checkbox so clicking the text toggles it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: select all drafts by default in draft compare Drafts now load pre-selected (deploy-all is the common intent); guarded so a reload after a deploy doesn't re-select the items left behind. Mirrors CompareWorkspaces' default auto-selection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: show diff for draft-only items stored without a draft row A draft_only flow/script/app whose content lives in the entity row itself (created via create*(draft_only: true), no separate draft-table row — like u/admin/new) returns draft == null from get*ByPathWithDraft. getDraftDiffValues passed that null through, so the diff "after" side was empty and nothing rendered. Fall back to the row's own value as the draft content when draft is null (deployDraft already did this), fixing both the compare-page DiffDrawer and the session bars' WorkspaceDiffDrawer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: shared diff button across session bars + bar spacing - Extract SessionDiffButton (variant=default, ± DiffIcon, count, "Open diff" title) and use it for the diff-drawer trigger in both the fork bar and the draft bar, so they're identical. Drop the icons from both "Review" buttons. - Add gap-1 (4px) between the fork bar and draft bar when both are visible (flex wrapper; single in-flow root per bar, drawer is portalled — no stray gap when only one shows). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: deploying a new (draft-only) flow or app A draft_only flow/app already has an entity row (created via create*(draft_only: true)), so deployDraft's createFlow/createApp rejected it with 400 "already exists". Use updateFlow/updateApp instead — a listed draft always has a row, and update promotes a draft_only entity to a real deployed version (clearing the flag), like the editor does. Scripts were unaffected (createScript + parent_hash makes a new version). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: refresh fork comparison after deploying/discarding a draft Deploying a draft promotes it to the workspace's deployed version, changing the fork comparison (ahead/behind vs parent) — but the compare page only re-fetched it on workspace change, so the deploy/update toggle counts and the CompareWorkspaces tab went stale. CompareDrafts now fires onChanged after a successful deploy/discard; the page rewires it to refresh the comparison and draft count. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: stop draft-count effect from freezing the AI session page ensureDraftCount cleared its dedupe key on error; since the caller is a reactive $effect (SessionDraftBar), a persistently-failing countDrafts spun the effect into an infinite retry loop that flooded the console and froze the tab. Claim the key before awaiting and keep it set on failure; refresh*() still forces a re-fetch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: correct draft count and refresh compare counts after actions count_drafts now counts deployable drafts (draft_only OR has-a-draft-row across script/flow/app), matching the CompareDrafts list, instead of raw draft-table rows which miss new draft-only items. CompareWorkspaces and CompareDrafts fire onChanged so the compare page re-fetches the comparison and draft count after deploy/update/discard, keeping the toggle badges in sync. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: session draft bar shows a fresh count on every (re)open The runtime persists across client-side navigation, so the deduped ensureDraftCount() kept a stale count (e.g. a 0 cached before a draft was created) when a session was re-opened — the bar stayed hidden even though the server count was >0. Force one fresh fetch per mount from a non-reactive onMount via refreshDraftCount(workspace) (which now takes the workspace so it works before the dedupe key is set). The reactive $effect keeps using ensureDraftCount: refreshDraftCount reads loadingDraftCount ($state), so calling it from an effect would track-and-mutate that state into an infinite fetch loop — ensureDraftCount's plain-key early-return avoids it. ensureDraftCount also now releases its key after a 5s backoff on failure so a transient countDrafts error retries instead of leaving the bar stuck. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor: make the draft count a single deep Workspace Drafts module The Draft Count was computed four ways (backend count_drafts SQL, the CompareDrafts list filter, a bespoke sessionRuntime cache, and the compare page state) that drifted — the root cause of the unreliable count, the stale-on-reopen bug, and the effect-loop freeze. Introduce one module (workspaceDrafts.svelte.ts): - getDraftItems(ws) lists the deployable Draft Items once; count ≡ list length, never a separate query. - useWorkspaceDrafts(() => ws) is a component-scoped runed resource (fetches on mount + ws change, no persistent cache → fresh on every (re)open). - invalidateWorkspaceDrafts(ws) refreshes mounted consumers; deployDraft/ discardDraft self-invalidate, so callers never reason about staleness. Rewire every reader to it (SessionDraftBar, CompareDrafts, DraftDiffDrawer, WorkspaceDraftsBanner, compare page) and delete the sessionRuntime draftCount apparatus (key + loading flag + 4 methods + effects + backoff). With no caller left, remove the count_drafts endpoint (handler, route, openapi, sqlx cache, generated client) — drafts.rs/openapi return to their main state. Record the draft vocabulary in CONTEXT.md. A single GET /w/{ws}/drafts/items endpoint can later replace getDraftItems' three list calls behind the unchanged seam. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: warn before deploying a draft based on an outdated version When a newer version is deployed while a draft exists (git-sync/CLI deploys preserve drafts via skip_draft_deletion), the compare/deploy-drafts page now flags the draft as Outdated and gates deploy behind an override confirmation with a diff — instead of silently clobbering the newer version. Staleness is read from the draft's base version: scripts already store parent_hash; flows/apps now record a draft_base_version sidecar on save. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: drop redundant /ui_builder proxyRes hack (superseded by #9433) main's global configure-response-headers plugin now runs with enforce:'pre' and sets COOP/COEP/CORP on dev responses (#9433), so the per-proxy proxyRes override is no longer needed. Revert the /ui_builder block to main's headers form — vite.config.js now matches main with no branch-specific change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sessions): keep draft count reactive to preview/chat deploys Invalidate the Workspace Drafts resource at every frontend deploy seam (ScriptEditorView / FlowEditorView / RawAppEditorView onDeploy + onSaveDraft) so user-driven deploys from the Preview panel update the count immediately, and refresh SessionDraftBar on the same coarse signals SessionForkBar uses (AI turn-end + tab refocus) to cover chat-driven deploys that happen server-side and never surface as frontend calls. Also: always show the draft toggle count including (0) on the compare page, drop the header/content separator line in both compare cards, and derive the "Deploy N drafts" footer count so it stays reactive after discard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor: address branch review findings - WorkspaceDraftsBanner: use the modern Button API (variant/unifiedSize/onclick) instead of the deprecated size/color/on:click triad; drop "pending" from the banner copy to match CONTEXT.md vocabulary. - WorkspaceDeployLayout: make Cmd/Ctrl-click distinct from a plain click. Plain row click now selects only that row (classic file-picker), Cmd/Ctrl toggles, Shift extends the range, and the checkbox still plain-toggles. Adds an onSelectOnly callback wired in CompareDrafts/CompareWorkspaces. - WorkspaceDiffDrawer: document why the file filter is a raw input (bespoke keyboard-nav integration the design-system inputs can't express). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * revert: drop draft version-gating (stale-draft warning) Remove the "deploying an outdated draft would override a newer version" guard. It's a rare edge case and will be handled properly by conflict resolution in a follow-up PR. - CompareDrafts: drop staleMap/computeStaleness, the TOCTOU pre-deploy re-check, the "Outdated" badge, the override-in-diff button, and the "Newer version deployed" confirmation modal; deploySelected is now the plain deploy. - utils_draft_deploy: remove getDraftStaleness/DraftStaleness and the draft_base_version strip. - FlowBuilder / AppEditorHeader / RawAppEditorHeader / AppJsonEditor: stop injecting draft_base_version into draft saves — these editor paths are back to matching main, shrinking the PR's blast radius. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * style: unify home banner CTAs on the modern Button API Both the Workspace Drafts banner and the sibling Fork banner now use variant="default" unifiedSize="sm" onclick, so the two CTAs on the home page render identically and neither uses the deprecated size/color/on:click props. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(compare): show draft deploy direction badge inside forks Mirror the fork compare header's "from → into" badges on the Deploy-draft tab: "deploy: draft → into: <fork>". Makes it explicit that deploying a draft promotes it within the fork (deployed↔draft), not up to the parent. Only rendered inside forks, where the parent could otherwise be confused with the deploy target. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(compare): address PR review — dedup drafts resource + shared link - De-dupe the compare page Workspace Drafts fetch: the page owns the single resource and passes draftItems/draftsLoading into CompareDrafts (was mounting a second resource → 6 list calls; now 3). - Prune transient deploymentStatus for items dropped from the list (no unbounded growth, no stale 'deployed' suppressing a re-drafted row). - Type getDraftItems' list fields via a narrow DraftListEntry (drop Array<any>). - Clear comparison catch-up timers on unmount (onDestroy). - Extract shared ExternalEditLink.svelte; use it in CompareDrafts, CompareWorkspaces, WorkspaceDiffDrawer (was a near-verbatim <a> block x3). - Note conflicts intentionally count in both toggle directions; drop a stray blank line in sessionRuntime. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(compare): show draft summary renames via shared item-summary component Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(compare): address round-2 PR review - Point the fork-compare edit link at the workspace the item actually lives in: a parent-only row (absent in the fork) would 404 if linked into the fork, so link it into the parent instead. - Replace the bespoke raw <button class="underline">Deploy drafts</button> in the undeployed-drafts alert with a design-system Button (variant=subtle). - Drop the stray Prettier reflow in sessionRuntime (restore to match main). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(compare): warn on fork items with a pending draft In the fork compare list, items that are deployed *and* have a pending draft (has_draft) now: - show a yellow "+Draft" badge (AlertTriangle), rendered before the New/status badges, with a per-direction tooltip explaining that deploying/updating moves the deployed version, not the draft; - are excluded from the default selection (still manually selectable); - trigger a confirmation modal if explicitly selected and deployed/updated, listing the affected paths. The signal comes from the page's existing fork drafts resource (a kind:path Set passed down) — no new fetch, no backend change. Also rename the undeployed-drafts alert CTA from "Deploy drafts" to "See drafts". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(compare): rename page to "Compare & Deploy" Update both the page heading (PageHeader) and the browser-tab title. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(compare): multi-select rows by default (no modifier) In the shared WorkspaceDeployLayout (fork + draft lists), a plain row click now toggles the item in/out of the selection instead of replacing the whole selection with it. Removed the modifier-based selection entirely: the now-dead onSelectOnly path and its two call sites, plus shift+click range selection (and its anchor/isPickable helpers). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(table): don't toggle row selection on keyboard child activation Row's onkeydown selection handler lacked the interactive-child guard that handleRowClick already had, so pressing Enter/Space on a checkbox, action button, or title link both activated the child and toggled the row's selection. Extract a shared fromInteractiveChild() guard and apply it in handleRowKeydown, mirroring the click path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(fork-banner): show draft CTA when fork is up to date When a fork has no changes vs its parent ("Everything is up to date") but has pending drafts, the banner now mirrors the non-fork drafts banner: the status text becomes "This workspace has N draft(s)" and the button becomes "Review & deploy drafts", linking to the compare page in draft mode. When the fork has real ahead/behind diffs, the existing status and buttons are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(compare): honor renamed draft paths + raw-app draft fixes Address the Codex review: - Draft deploy now uses the draft payload's path for scripts, flows and raw apps (keeping the URL path as the existing item key), so a rename in a draft deploys to the new path instead of silently staying at the old one. - DraftDiffDrawer maps raw apps to the `raw_app` kind so their row edit links open the raw-app editor, not the legacy app editor. - ScriptEditorView.restoreDeployed invalidates the workspace drafts after deleting the draft, so the session draft-bar count drops immediately. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(compare): guard showDiff race, tree label, mode fallback Address the cubic review: - CompareDrafts.showDiff uses a monotonic request token so two quick "Show diff" clicks can't let a slow earlier fetch overwrite a faster later one. - WorkspaceDiffDrawer.buildTree labels a 2-segment path with its leaf name (parts[1]) instead of the full scope key. - The compare page only resolves ?mode=draft immediately; ?mode=fork (and an absent mode) defer to the isFork-aware effect, which falls back to draft for non-fork workspaces instead of stranding them on the fork UI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: remove CONTEXT.md from the PR Drop the root CONTEXT.md domain glossary and the lone comment pointer to it in workspaceDrafts.svelte.ts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(compare): send custom_path on raw-app draft deploy A raw-app draft that changes or clears its custom route was silently dropped on deploy from the compare page: updateAppRaw omitted custom_path, so the backend preserved the old route. Send the draft's custom_path on update — matching the fork deploy path (which spreads the full app, custom_path included) and the createAppRaw branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): refresh draft count on raw-app session save-draft The script/flow session editors invalidate the workspace drafts on save-draft, but the raw-app editor only did so on deploy. Thread an onSaveDraft callback through RawAppEditor → RawAppEditorHeader and call invalidateWorkspaceDrafts from RawAppEditorView, so saving a raw-app draft in an AI session updates the SessionDraftBar count immediately (and the bar appears when the count was zero). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(compare): honor renamed paths, draft triggers & paginate inventory Address the Codex review: - Draft deploy honors the draft's renamed path for scripts/flows/raw apps (keeping the URL path as the existing item key). - Script/flow draft deploy now deploys draft_triggers via the shared deployTriggers, instead of silently dropping them with the draft. - rawAppDeploy sends custom_path admin-gated on update (admin: value/'' to clear; non-admin: undefined) so non-admins don't hit RequireAdmin. - getDraftItems pages through listScripts/listFlows/listApps so drafts past the first page are included in the count, banners, drawer and deploy list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(compare): admin-gate custom_path on visual-app draft deploy The visual-app branch of deployDraft sent custom_path unconditionally on updateApp, so a non-admin deploying an app draft for an app with a custom route hit RequireAdmin. Mirror AppEditorHeader and the raw-app path: admins send the draft's custom_path ('' clears), non-admins send undefined so the backend preserves the existing route. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(raw-app): save initial draft directly when path is known In the AI-session preview, a never-deployed raw app has newApp=true but a known path, so saveDraft opened the "Initial draft save" path-picker drawer — which is gated on `appPath == ''` and therefore never rendered, making Save draft silently do nothing. Branch the new-app case on appPath: pick a path via the drawer only when none is chosen yet; otherwise call saveInitialDraft() directly. saveInitialDraft now also toasts and fires onSaveDraft so the session draft-bar count refreshes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(compare): preserve deployed custom_path on visual-app draft deploy The visual-app draft value usually omits custom_path, so the admin branch's `d.custom_path ?? ''` sent an empty string, which the backend treats as "clear the route" — an admin deploying a content-only draft would wipe the app's existing custom route. Fall back to the deployed route (`r.custom_path`) when the draft omits it; an explicit '' still clears. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
a3740d571a |
chore(main): release 1.720.0 (#9464)
* chore(main): release 1.720.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.720.0 |
||
|
|
e8e0701a36 |
feat(api): add endpoint to update token label (#9474)
* feat(api): add endpoint to update token label Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(api): prevent renaming the session token label Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(api): restrict token-label edits to user tokens, not just session Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): edit token label in the edit modal instead of inline Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(api): reject relabeling tokens to reserved system-token names Centralize the is_user_token classifier in windmill-common and reuse it to reject labels colliding with system-token namespaces (ephemeral*, debugger-token, mcp-oauth-*), not just session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(api): match ephemeral label case-insensitively and cap label length Align the canonical is_user_token, the SQL guard and the frontend mirror on a case-insensitive `ephemeral` match (so a token can't be relabeled to a casing the backend allows but the UI hides), reject labels over the VARCHAR(1000) column limit with a 400, and add unit tests for is_user_token. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5d0ef7dfd9 |
fix: center auth0/okta icons and respect currentColor (#9457)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
6d522b3989 |
fix: refresh session editor preview on breadcrumb target switch (#9475)
* fix: refresh session editor preview on breadcrumb target switch
Consolidate the three session editor views into a SessionEditorTarget deep module that remounts the heavy editor on a data-ready target swap ({#key slot.loadedPath}), so stale mount-time state (e.g. Path.svelte's settings-panel path) re-derives. Adds LoadSlot to the runtime and a useUserDraftSync composable + per-kind codecs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: flush pending session draft write on target switch
A breadcrumb target swap (or unmount) within the 150ms outbound debounce window cleared the pending UserDraft write instead of flushing it, dropping the last edits. Scripts previously saved immediately so this was a regression from the new uniform debounce; flow/raw_app already had the latent drop. A dedicated path/workspace-scoped effect now flushes the pending write on switch/unmount without disturbing the debounce during a typing burst. Also refreshes a stale loadScript comment that named removed symbols (addresses PR review nits).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
192574ab8f |
fix(forks): keep trigger/schedule operational state owned by the parent - WIN-2019 (#9476)
* fix(forks): defer trigger/schedule state to parent for clean git merge Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(forks): read parent trigger/schedule state on non-RLS pool for complete substitution Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(forks): read schedule fork-ness on non-RLS pool; clarify mutator-rule wording Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
76c0d970a1 |
fix(oauth): persist refreshed token through configured secret backend (#9471)
The lazy on-fetch OAuth token refresh persisted the new access token with a raw
`UPDATE variable SET value = <db-encrypted>`, bypassing the secret-backend
abstraction. With an external secret backend (AWS Secrets Manager / Azure Key
Vault / Vault), secret reads resolve through the backend and ignore
`variable.value` entirely, so refresh advanced `account.expires_at` and updated
Postgres but never wrote the new token to the external store. Every read that
did not itself trigger a mint kept serving the frozen connect-time token, which
expired ~1h after connect (RefreshError on Google clients).
`windmill-oauth` can't depend on `windmill-store` (circular), so variable
persistence moves out of `refresh_token{,_for_account}` (which now only exchange
the token + update the `account` row and return the new token) into the
`windmill-store` callers, via a new `store_oauth_token_value` helper that writes
through the configured backend and stores the returned value (encrypted blob for
the DB backend, `$...:` marker for external backends) in `variable.value`.
If persisting the refreshed token fails (more likely now that it can be a
network write to an external backend) after the account was committed fresh,
`store_oauth_token_value` resets `expires_at` to the past and records
`refresh_error` — looking the account up via `variable.account` — so the next
fetch retries instead of serving the stale token for the whole token lifetime.
Also add `windmill-store/tests/oauth_refresh_secret_backend.rs`, an opt-in e2e
regression suite (RUN_SECRET_BACKEND_E2E / RUN_AWS_SM_TESTS) covering database
and external (AWS SM via LocalStack) backends plus the self-healing reset.
Verified against Postgres + LocalStack: 3 passed.
EE companion (oauth_refresh_ee.rs: 3 refresh paths) merged via #607; this OSS
half completes the fix (ee-repo-ref already at EE main 481ea7f).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
fa86c62b66 |
fix(frontend): use ban icon for canceled jobs instead of hourglass (#9478)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3bc5800197 |
feat: allow private MCP server URLs (#9470)
* feat: allow private MCP server URLs * docs: remove private MCP server URL doc * fix: apply MCP URL opt-in to OAuth handlers * fix: update EE ref for MCP OAuth redirects * fix: preserve MCP OAuth client timeout * chore: update ee-repo-ref to 481ea7f28dc5af6b72390c82f494f34cb9809546 This commit updates the EE repository reference after PR #608 was merged in windmill-ee-private. Previous ee-repo-ref: 6c7da03fb994be23ed6aca59bece94d257a641b5 New ee-repo-ref: 481ea7f28dc5af6b72390c82f494f34cb9809546 Automated by sync-ee-ref workflow. --------- Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
82cb7bf375 |
whitelabel default timeout + test-job callbacks (#9469)
Add a configurable `defaultTimeout` to the script/flow editor whitelabel customUi (replaces the hardcoded 300s default) and an `onTestJob` callback on ScriptBuilder/FlowBuilder that fires with the preview job id when a test run starts. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
64b089cd23 |
feat(frontend): use unified drill picker for AI chat @-mention dropdown (#9159)
* feat(frontend): use unified drill picker for AI chat @-mention dropdown * fix(frontend): chat picker review followups + overlay alignment - AIChatDisplay: migrate @-badge popover to ChatContextPicker (was still importing the deleted AvailableContextList after the rebase onto #9034, causing a build break). - DrillPicker: handle Tab as Enter so the inline @<word> mention completes without losing focus. Tweak leaf-row weight to font-normal; secondary text uses text-hint. - ContextTextarea: drop px-0.5 from the highlight span — extra horizontal padding made every glyph typed after a mention drift right of the invisible textarea below. box-decoration-clone keeps the rounded corners. - ContextElementBadge: explicit font-normal label, hoist label into a {@const} and pass to title= so the truncated badge shows the full title on hover. - workspaceTree: drop orphaned doc-comment left dangling by the rebase. - Add unit tests for drillPicker.ts and workspaceTree.ts (51 tests cover resolveScope/scopeChain/collectLeavesGrouped/leafHaystack, buildWorkspaceTree shape + loading + dir forest + leaf shape, withCurrent rename suppression, extraItemsByKind dedup, legacyScopeToPath, relativizeWorkspacePath). * fix(flow-editor): ignore keyboard shortcuts when focus is outside the flow root Menus, modals, drawers etc. live outside the flow root and capture focus explicitly. Flow nodes aren't focusable, so the unfocused default (activeElement === body) means "flow is the canvas" and we should react; anything else means another surface has the user's attention and our shortcuts would steal it. * fix(frontend): inline @ mention picker + chat layout polish - ContextTextarea: swap manual Portal+caret-math positioning for svelte-floating-ui anchored at the `@` character (virtual reference, middleware [offset, flip(crossAxis:false), shift]). Picker stays pinned to `@` while the user types the query, slides leftward when hitting the right edge instead of flipping alignment, and floating-ui handles above-vs-below + edge clamping automatically. Drops the 60vh-worst-case reservation that left a big gap above the caret in sessions, and the now-unused isFirstMessage prop is marked deprecated. - AIChatDisplay: the `@`-button Popover now opens with placement bottom-start (was the default `bottom`), aligning its left edge with the button instead of centering under it. - ChatContextPicker: when no Diffs/Modules/Databases branches are present (e.g. global chat), return the Workspace tree's children at the root instead of wrapping them under a redundant "Workspace" row. handleScopeChange handles both the wrapped and unwrapped layouts and the single-kind `dir:` top segment. * chore(frontend): address review suggestions on chat picker PR - DrillPicker: clamp width to viewport on narrow screens — w-[420px] → w-[min(420px,calc(100vw-20px))]. - workspaceTree.buildWorkspaceTree: make loadingKind optional (defaults to {}). Chat picker still passes it; callers that don't track loading no longer need to thread an empty object. - ChatContextPicker.handleScopeChange: name the WRAPPED vs UNWRAPPED layouts in a comment block so the dir:/kind: branches are obvious. - ContextTextarea: drop deprecated isFirstMessage prop (floating-ui handles direction); drop defensive Math.max on the @ index now that the invariant is documented; comment the floatingRef(anchorRef) call as the supported virtual-reference path in svelte-floating-ui. - AIChatInput: stop forwarding isFirstMessage to ContextTextarea. * feat(frontend): sync selectedContext with @-mentions in textarea Both picker entry points now insert a visible `@title` token in the textarea, and deleting that token drops the matching entry from selectedContext. - AIChatInput: new insertMention(title) export. Appends `@title ` to instructions, prefixing a space only if the existing text doesn't already end in whitespace. - AIChatDisplay: the `@`-button popover calls insertMention after addContextToSelection so its picks match the inline-mention path's textarea state. - ContextTextarea: new onRemoveContext callback. A $effect compares the set of `@title` tokens in `value` (derived) against the previous snapshot; titles that disappeared trigger onRemoveContext for any selectedContext entry with `deletable !== false`. The diff lives in an effect (not handleInput) so it catches both keystroke deletions AND programmatic value updates from updateInstructionsWithContext. - AIChatInput: passes onRemoveContext that filters selectedContext by type+title — mirrors the existing badge X-button handler. * chore(frontend): narrow ChatContextPicker `inner` from `any` to `DrillPicker | undefined` The previous `let inner: any` worked around svelte-check rejecting `DrillPicker<ChatLeafData>` (the imported component is seen as the non-generic `Comp`). Dropping the type parameter keeps the workaround without `any`, so handleKeydown / pickHighlighted are at least typed at the call site. Addresses May-14 PR review. * fix(frontend): address PR #9159 bot-review findings (eager preload, focus, dedup, icon types) - [P1] ChatContextPicker.handleScopeChange: stop preloading workspace kinds at the wrapped picker root. New `isWorkspaceOnly` $derived (true when no Diffs/Modules/Databases branches are present) gates the at- root preload, so the chat root no longer fires two list requests before the user enters Workspace. Reported by Codex. - [P2] AIChatDisplay @-button popover: call aiChatInput.focusInput() after close() so the textarea is focused for immediate typing — mirrors the inline-mention path's setTimeout(textarea.focus, 0). Reported by Claude. - [P2] AIChatInput.insertMention: no-op when the `@title` token is already present in instructions, so re-picking a workspace item doesn't leave duplicate visible tokens for a single selectedContext entry. Reported by Codex. - [P2] drillPicker.ts: introduce `DrillIcon = ComponentType | Component<any, {}, ''>` and replace `icon: any` on DrillLeaf, DrillBranch, and ChatContextPicker.buildContextBranch. Mirrors the ComponentType | Component pattern used in TriggersBadge.svelte for the same Svelte 4/5 compatibility window. Reported by Pi. * fix(frontend): preserve workspace context on refresh + load all kinds for internal search - [P1, Codex] ContextManager.updateAvailableContextForScript/Flow: preserve workspace_script and workspace_flow entries through the selectedContext filter on editor refresh. They're user-picked refs that don't appear in availableContext, so the previous filter was silently dropping them whenever the script/flow editor refreshed options (e.g. on any code change). - [P2, cubic-dev-ai] WorkspaceItemDrillPicker: in internal-search mode (externalFilter === undefined, DrillPicker renders its own search box), preload all kinds on mount. Without this, typing in the picker's search before clicking a kind branch produced incomplete results since DrillPicker can't reach back through the adapter to trigger fetches on internalFilter change. Cached items keep the effective cost near-zero on warm sessions. * fix(frontend): preserve workspace refs through script-mode context refresh The script-mode updateAvailableContext overwrites newSelectedContext with a fresh [code] entry, defeating the workspace_script / workspace_flow preservation in the later filter — the entries are already gone by the time the filter runs. Seed newSelectedContext with the refreshed code block AND the user- picked workspace_script / workspace_flow / code_piece entries from currentlySelectedContext, so editor refreshes don't wipe @-mention badges in script chat. The existing line-271 filter still validates each entry against newAvailableContext + the per-type allowlist. Reported by Codex on PR #9159 — completes the prior workspace-context- on-refresh fix (b02d1f2d35) which only patched the filter, not the rebuild step that runs before it. * fix(frontend): preserve all previously-selected contexts on script refresh The prior c2775fe0c5 fix only carried over workspace_script / workspace_flow / code_piece entries from currentlySelectedContext. That preserved the workspace P1 path but still dropped previously- selected diff / error / db / runtime-context badges, which cubic flagged in its 16:55 review. Spread the full currentlySelectedContext (minus `code`, which we just rebuilt). The downstream filter validates each entry against newAvailableContext + the per-type allowlist, so auto-derived types like diff / error / db survive when still applicable, and unrelated items are dropped automatically. Reported by cubic-dev-ai on PR #9159. * fix(frontend): rehydrate auto-derived context + sync badge X with textarea - [P2, cubic] ContextManager.updateAvailableContext: when the rebuild carries over previously-selected diff/error/db entries, swap each one for the matching freshly-built entry from newAvailableContext in the final .map() step. Preserves the user's `deletable` override on top of the fresh content/diff/schema, so refreshes don't keep stale payloads while still surviving the badge across edits. - [P2, Pi/Codex] AIChatInput: new `removeMention(title)` export that strips `@title` tokens from `instructions` (whitespace-bounded so substring matches don't bleed). The badge X-button now calls it after filtering selectedContext, mirroring the inverse textarea-to- badge sync. No double-remove: ContextTextarea's $effect-driven onRemoveContext is a no-op once selectedContext no longer holds the entry. * fix(frontend): retype ChatContextPicker.inner to DrillPicker<ChatLeafData> `npm run check:fast` (TypeScript-only) and `npm run check` (svelte-check) disagree on whether the imported DrillPicker is generic — `check:fast` sees it as `Comp` and rejects the type parameter, while `svelte-check` sees the real generic component and requires it. CI runs `check`, so follow that: `DrillPicker<ChatLeafData> | undefined`. This also fully replaces the prior `inner: any` workaround called out in multiple bot reviews — handleKeydown / pickHighlighted now type-check at the call site against the correct component instance. * fix(frontend): scope removeMention's whitespace collapse to the mention site The trailing `.replace(/ +/g, ' ')` in `removeMention` was global, collapsing any pre-existing double-spaces in the prompt — e.g. a user typing `"hello world @foo bar"` lost their intentional formatting when they deleted the `@foo` badge. Rework the regex to match `(^|\s)@title(\s|$)` and decide per-match: - Mention at a boundary (no lead or no trail): drop entirely. - Mention in the middle: keep ONE bordering whitespace char (the leading one verbatim, so newlines/tabs aren't downgraded to spaces). No global pass over `instructions`. Unrelated whitespace stays intact. Reported by cubic-dev-ai on PR #9159 (07:27 review of 9e07eac4). * fix(frontend): expose DrillPicker.onFilterChange + lazy-load workspace kinds Both Codex P1s came from over-eager preload heuristics on my prior fixes: the workspace picker cold-loaded every configured kind on mount in internal-filter mode, and the chat badge popover never observed its own internal filter so workspace results were missing from search until the user drilled into Workspace. Replace both ad-hoc effects with a single `onFilterChange` callback on DrillPicker that fires whenever the EFFECTIVE filter (external or internal) changes: - [P1] WorkspaceItemDrillPicker: drop the "cold-load on mount when externalFilter === undefined" effect. Workspace kinds now load only once the user actually types something — closer to the pre-refactor behavior where the breadcrumb / "Open editor" pickers only fetched the drilled-into kind plus all kinds on search. - [P1] ChatContextPicker: handleFilterChange replaces the prior externalFilter-only effect. Badge-popover search (internal filter) now triggers the same preload as inline-mention search (external filter), so workspace results appear without needing to drill first. Both fixes reported by Codex on PR #9159. * fix(frontend): skip mention-removal sync when textarea is programmatically cleared sendRequest() sets `instructions = ''` immediately after dispatching to AIChatManager. The mention-removal effect treated this as user-initiated deletion and cleared selectedContext BEFORE AIChatManager.beforeSend snapshotted it — selected `@` contexts disappeared from the outgoing request. Skip the sync when value is empty; user-initiated mention deletes happen in-place against non-empty content. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(frontend): scope post-send wipe protection to the send path only Replace the blanket `if (value !== '')` guard on the mention-removal effect with an explicit `clearForSend()` export. `sendRequest()` now calls it instead of `instructions = ''`, so a user manually clearing the whole textarea still drops the corresponding context badges while the post-dispatch programmatic wipe is silent. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(frontend): extract useWorkspaceItemsLoader composable shared by both drill picker adapters WorkspaceItemDrillPicker and ChatContextPicker each duplicated the same machinery: loaded/loadingKind state seeded from the module cache, a stale-while-revalidate ensureLoaded coroutine with an untrack guard, a kind:/dir: scope-segment decoder, and the "load every kind once the user starts searching" filter callback. Move that to a single useWorkspaceItemsLoader() returning {loaded, loadingKind, ensureLoaded, ensureAll, ensureForScopeSegment, onFilterChange}. Adapters keep their own scope-walking policy (chat collapses an optional 'workspace' wrapper, workspace handles single-kind mode) but delegate kind decoding and lazy fetch to the composable. Net: -135 +28 LOC in the two adapters; +109 LOC in the new composable. The cache-version race, untrack discipline, and stale-while-revalidate semantics now live in one place. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(frontend): address Codex P1+P2s — non-context clear, same-title cross-removal, single-kind cold load P1: sendRequest() now clears `instructions` unconditionally after the optional `clearForSend()` so APP/NAVIGATOR/ASK/API modes (which don't mount ContextTextarea) still reset the input after send. P2: removeMention() now calls a new `unsyncMention(title)` on the textarea before stripping `@title` from `value`, so the mention-removal effect doesn't fire a second onRemoveContext on a same-title sibling (e.g. workspace_script + workspace_flow sharing a path). P2: single-kind WorkspaceItemDrillPicker loads its kind at mount even when scope is empty — buildWorkspaceTree collapses to the kind's children, so there's no kind row to drill into to trigger the load. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
6156e2372a |
fix: gate native integration pickers behind non-operator check (#9465)
Operators are read-only and cannot create native triggers, yet the google/github/nextcloud integration picker routes had no authorization gate, letting any workspace member drive the admin-configured integration's upstream API and enumerate its data (Drive files, repo names, calendars, events). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
44f5dd6636 | fix(frontend): respect forced column order for numeric column names (#9463) | ||
|
|
004339032e |
chore(main): release 1.719.0 (#9459)
* chore(main): release 1.719.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.719.0 |
||
|
|
fbdf81ba5f |
fix: authenticate slack callback payload with per-workspace hmac (#9461)
* fix: authenticate slack callback payload with per-workspace hmac Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: regression tests for unauthenticated slack callback decryption Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: verify slack submission signature before resume + close workspace oracle Addresses review: verify private_metadata HMAC before handle_resume_action so a tampered/unsigned submission is rejected up front, and map get_workspace_key failure to the generic 401 so the status code is not a workspace-existence oracle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: domain-separate slack payload hmac from resume-secret hmac Both MAC families key Hmac<Sha256> on the same per-workspace key; resume secrets are distributed to approvers in resume URLs, so add a fixed domain tag (slack_payload_v1) to the slack payload MAC to make the two non-interchangeable by construction rather than by byte-layout coincidence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6a15a9b152 |
fix(python): escape reserved-keyword step ids in wrapper codegen (#9460)
A flow inline step whose id is a Python keyword (e.g. `in`) crashed with a
`SyntaxError`: the wrapper emits `from {pkg} import {step_id} as inner_script`,
and `from x import in as y` is invalid Python.
The codegen already prefixes `_` to path segments that start with a digit
(`1234` → `_1234`); this extends that guard to Python hard keywords (`in` →
`_in`) in `compute_python_module_dir` and on the leaf in `compute_py_codegen`
and `prepare_wrapper`. The relative-imports write path inherits it for free.
Fixes #8893
|
||
|
|
e1e7af6a25 |
fix: prevent token label collision bypassing job read access control (#9462)
* fix: prevent token label collision bypassing job read access control Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: regression tests for token label collision job read access Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: bind job-read override fast-path to permissioned_as_email Replaces the reserved-label / label-* exclusion approach: webhook-/http-/email- labels are created through the public token API by the trigger panels, so they cannot be reserved, and blocking label-* regressed legitimate re-reads. Instead the username_override fast-path now requires the job's permissioned_as_email (non-forgeable, never derived from the label) to equal the caller's email. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fad1a549d9 |
feat(otel): connect jobs to the inbound distributed trace (#9456)
* feat(otel): propagate inbound W3C traceparent to job spans Capture the inbound traceparent header at the run endpoints (WebhookArgs::to_args_from_format) into a reserved _wm_traceparent arg key (gated on OTEL_TRACING_ENABLED), riding the args jsonb like _ENTRYPOINT_OVERRIDE. At pickup, create_span_with_name attaches a span link from the job's worker span to the originating distributed trace, so a job triggered by an instrumented service is connected to the caller's trace while keeping its UUID-derived trace id (trace-by-job-id unaffected). The link/parse logic lives in the EE otel modules; this OSS side only captures the header and calls the (no-op outside EE) hook. Companion EE PR required. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump ee-repo-ref to inbound-trace-propagation EE branch Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(agents): don't attribute work to specific customers in repo content Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(otel): relocate job + script spans into the inbound trace Builds on the captured _wm_traceparent: the worker job span is re-parented on the inbound caller context, the script subprocess's TRACEPARENT env is the inbound context (so its spans join the caller's trace), and the context is propagated to flow steps so the whole flow relocates. Carried to the worker via a new LogContext.inbound_traceparent field. Non-inbound jobs are unchanged. Adds a relocation integration test. Companion EE PR required. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump ee-repo-ref to inbound-trace-propagation relocate commit Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(otel): harden inbound traceparent capture Address review feedback: - strip any caller-supplied _wm_traceparent from args/extra before stashing the header-captured value, so the reserved key is Windmill-controlled only - valid_w3c_traceparent: reject version ff and require lowercase hex, so we don't forward an inbound header that downstream OTel parsers would reject - clarify that the capture helper does not validate the W3C format (done at use) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 2c7964460327fab5e3a27c0f74b8d6f26ab7f79a This commit updates the EE repository reference after PR #604 was merged in windmill-ee-private. Previous ee-repo-ref: 8fc04fb105dc49769205f7174d551a0d134d1bec New ee-repo-ref: 2c7964460327fab5e3a27c0f74b8d6f26ab7f79a 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> |