mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-06 00:02:13 +00:00
af8ff3868748412cb658c803ebc8a71edc3cd8fb
8913
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
af8ff38687 |
fix: tolerate string app_id in GHES app config deserialization (#10923)
* fix: tolerate string app_id in GHES app config deserialization Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Rh73nHumzCbyd4Gwf6kw6 * fix: address review — strict app_id validation, drop dead variant Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Rh73nHumzCbyd4Gwf6kw6 * chore: update ee-repo-ref to b52c6471d517d979a9887f207a36347b1af376c8 This commit updates the EE repository reference after PR #767 was merged in windmill-ee-private. Previous ee-repo-ref: ab2dc653719f9d65eb10964d1e2b5bc1b94d6535 New ee-repo-ref: b52c6471d517d979a9887f207a36347b1af376c8 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> |
||
|
|
9074de25ea |
fix: resolve chat path links against the session's operating workspace (#10924)
* fix: resolve chat path links against the session's operating workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RGq2deVkz8qnpfssssKzn7 * fix: hide the chat link drawer button where nothing can open it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RGq2deVkz8qnpfssssKzn7 * fix: hide the chat tool card open button where nothing can open it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RGq2deVkz8qnpfssssKzn7 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5d5ad4e897 |
feat: edit folders and groups in a drawer that saves once (#10873)
* fix: portal the confirmation modal so drawers cannot cover it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * fix: log a folder acl grant under the permission it granted Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * fix: keep a table's actions column at its right edge Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * feat: edit a folder in a drawer that saves once Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * refactor: call the people on a folder or item members Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * fix: edit a folder against the workspace the drawer targets Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * refactor: drop the now-unused sticky actions column Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * docs: correct the script editor drawer's modal placement note Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * fix: pin the actions column without losing the row's hover tint Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * feat: show the pinned column's seam only while the table overflows Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * fix: draw the pinned column's seam as a shadow so it does not scroll away Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * fix: fade the pinned column's tint in step with its row Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * refactor: address review nits on the folder editor and pinned cell Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * fix: keep the folder draft across a user-store refresh Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * refactor: extract and test the folder draft's dirty check and permission diff Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * fix: stop the folder editor showing state the server refused Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * fix: keep a folder draft that no request ever reached the server Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UCPLT4t8PmrWunjfFsPW * fix: keep unapplied folder edits dirty when a save partially fails * fix: block folder form edits while a save is in flight * fix: commit a typed folder label before save snapshots the draft * fix: count a typed folder label as an unsaved change * fix: keep escape in the label input from closing what encloses it * fix: capitalize folder table headers and drop a dead portal target * refactor: make the confirmation modal portal opt-in per call site * docs: name the stacking context that actually traps the discard dialog * fix: report a half-landed member removal so the baseline reconciles * feat: edit a group in a drawer that saves once * fix: freeze the group name once the group exists * fix: revoke the caller's own group acl last so the rest of the save is authorized * docs: state the group call-ordering invariant once * fix: report a failing post-save reload instead of dropping the rejection * fix: hand the folder list reload back so a failure is reported * fix: treat a rejected group create as inconclusive and catch a throwing onSaved * revert: stop inferring a group was created from its name being taken * fix: say when a failed group create may have saved the group anyway * fix: key the may-have-been-created hint on the name conflict, not the status * fix: skip the may-have-been-created hint when the group is known to exist * feat: open a folder's group member from its row * fix: stop showing the caller as an admin when the read failed * fix: give up the caller's own folder admin last, and label a create as one * fix: drop a folder member's acl before its owner entry * fix: remove a folder owner before their acl, and correct the rls rationale * docs: say the refusal is on the caller's last admin handle * fix: defer only the folder rows the caller is an admin through * docs: describe callerOwners as what the caller passes in * docs: drop the call-site restatement of the diff's own invariant * docs: record manager as a legacy group role * fix: treat a sent request as possibly committed when reconciling * fix: reconcile on any failed edit, and compare members as a set * fix: keep write access when only the reconcile read fails --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
816dc9dcd2 |
feat(ai-sessions): show a running session across tabs and reload finished turns (#10916)
* fix(ai-chat): make a disabled composer look disabled Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(ai-sessions): show a running session across tabs and reload finished turns Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): keep queued drafts through catch-up and hold locks by identity Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): carry pastes through refusals, spare resends and auto-resume Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): retry held auto-resume, keep the footer, spare bfcache freezes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): give each driving tab its own lock slot Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): release refused synthetic sends and use a text key separator Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): merge late-refusal restores and keep attachment-only edits Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): patch the stored chat pointer instead of rewriting the record Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * docs(ai-sessions): align the run-signal comments with the code Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): retry transient catch-up skips and gate the remaining send paths Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * docs(ai-sessions): name the chat-id seeding path persistTouched defers to Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4808f21b6b | add 180 and 365 day token expiration options (#10920) | ||
|
|
cfcfe298dd |
feat(ai-chat): make reusable skills ai_skill resources you select per workspace (#10914)
* feat(ai-chat): make reusable skills ai_skill resources you select per workspace * chore: pin the ee ref to the skill telemetry counters * fix: address review findings on skill authoring, import and migration * fix: enforce skill selection in read_skill and stop imports clobbering resources * feat: carry format_extension from the hub into synced resource types * fix: let an edit set or clear a resource type's format_extension * fix: regenerate the sqlx cache and close the review round findings * fix: close the round-2 findings on folder ACLs, cached sync and truncation * refactor: make the skills migration non-destructive and use design-system inputs * fix: close the round-4 findings on folder owners, startup sync and truncation * fix: clear obsolete extensions, guard folder owners, and report skipped skills * fix: honor explicit-null extensions and report same-type migration conflicts * fix: scope skill actions to the committed workspace and paginate the listing * fix: keep the drawer scoped to the live workspace and surface truncation * fix: discard a skills refresh for a workspace the chat has left * chore: update ee-repo-ref to 6efe7a73c745c2e1377a34498523c00d89010a3d This commit updates the EE repository reference after PR #764 was merged in windmill-ee-private. Previous ee-repo-ref: 55998c142bc72edd08532748af1974b16035658d New ee-repo-ref: 6efe7a73c745c2e1377a34498523c00d89010a3d Automated by sync-ee-ref workflow. --------- Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
870f67121d |
chore(main): release 1.800.1 (#10910)
* chore(main): release 1.800.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
c512110a1f |
fix(ai-chat): consume an @ mention with the message that carried it (#10907)
An `@`-mentioned workspace item stayed in `selectedContext` after the message that mentioned it was sent, so every later turn in the session restamped it into `## SELECTED CONTEXT`. Treat those mentions the way a DOM pick is treated: attached to the one message that carried them. The composer pins the live selection as `contextOverride` at the click and clears the mentions in the same synchronous gesture, so the send keeps what the user picked for it and the next draft starts clean. When a send hands its text back to the composer, the mentions it carried come back with it. Scoped to GLOBAL. In SCRIPT/FLOW/APP the mentions still stay selected as chips the user removes by hand, so `isMentionContext` is membership only and every caller gates on mode. Claude-Session: https://claude.ai/code/session_01HxGz1YsvW5Kwmn8THAUrwB Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
bedf5ae574 |
fix: add top margin to the home Build with AI section (#10909)
Claude-Session: https://claude.ai/code/session_01PkWNw5QJza9m16efArTYMr Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
412eb90c0d |
chore(main): release 1.800.0 (#10888)
* chore(main): release 1.800.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
716ce2ece0 |
feat: free AI tokens + home search/filter revamp (#10020)
* feat: add free Claude Opus tier with per-user token limit Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * nit move alert * Home AI Chat * wire home ai chat * auto send prompt * refactor: remove keyboard arrow-navigation from home list Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: replace home search bar with unified FilterSearchbar Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: replace home quick tags with FilterSearchbar presets Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: add content filter to home FilterSearchbar with EE-gated content view - Clear the kind filter by deleting the key (was showing a 'kind: null' tag on All) - Remove the standalone Content button - Add a 'content' filter; when set, render the Ctrl-K content-search view (ContentSearchInner) which shows text-match snippets and its own EE warning Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: disable home AI chat and prompt to configure AI when no model Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * track cost instead of tokens * nit * fix: load copilot config on home so AI chat isn't wrongly gated Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Home page update * nits * example prompts * nit * feat: switch free AI tier to DeepSeek with daily cost budgets Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * nit * Move bottom buttons to HomeAIChat * [ee] feat: surface free AI tier state and make its metering abort-proof Makes the free Windmill AI tier legible to the user and closes an abuse hole. Backend: - AIConfig gains a response-only free_tier marker (skip_deserializing so a client can't store a forged one via edit_copilot_config). get_copilot_info keeps returning it once the grant is spent, so the client knows AI is off because the grant ran out, not because nothing was configured. - Per-user grant becomes one-time (migration drops the day key from ai_free_token_usage); the daily table stays as the instance kill-switch. - Reserve-then-reconcile metering (see EE commit) so a mid-stream disconnect can no longer dodge the usage report and get metered zero. Frontend: - copilotInfo carries freeTier; model settings show a "Free" pill and a usage meter that warns past 80%. - The home chat and the session chat show a dedicated "you've used your free Windmill AI, add your own API key" state instead of the generic "no provider configured" one. - A failed send re-fetches copilot_info so the exhausted state (and its banner) appears live, without a page reload. Bumps ee-repo-ref.txt to the matching EE commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: free AI usage meter reusing the context-usage gauge Show free-tier spend with the same gauge as context usage instead of a bespoke block: - Extract the meter+tooltip into a shared UsageMeter; ContextUsageIndicator uses it, and a new FreeTierUsageIndicator renders it from copilotInfo.freeTier. Placed in the session-chat toolbar and next to the home-chat model settings; the old meter block in the model-settings dropdown is removed (the "Free" pill stays). - Hide the context-usage bar while on the free tier so the free meter takes that slot. - Refresh copilotInfo after every free-tier turn (AIChatManager finally) so the meter advances live and the turn that exhausts the grant flips to the exhausted state, instead of both only updating on reload. Gated to active free-tier users, so it costs nothing for configured-key users. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: fix stale free-tier comments after DeepSeek/cost rework Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: always show context bar, replace free-tier meter with usage banner Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * nit * fix: atomic free-tier budget reservation (ee ref + sqlx) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep CLI/MCP and Hub buttons unblurred on AI chat hover Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add back arrow nav * nit * nit * fix: three review P1s in the home AI chat & search - AIChatManager: refreshFreeTierUsage now bails unless the global copilot state still belongs to the completing manager's workspace, so a warm session finishing after a workspace switch can't reload its (background) workspace over the active one's models/client/copilotWorkspace. - HomeAIChat: block submission until the copilot config is loaded AND enabled (new `canSend`), so a prompt submitted during the unknown-config window isn't handed to a session that never sends it and silently lost. The disabled overlay still gates on config-loaded to avoid a flash. - ItemsList: the content-search reload effect now depends on $workspaceStore so content results follow the active workspace instead of showing the previous one's. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [ee] fix: harden the three home-AI-chat/search P1s after deeper review Follow-up to the previous P1 commit; sharper review found the earlier guards insufficient: - refreshFreeTierUsage now compares against the most-recently-*requested* workspace (new copilotWorkspaceRequested in aiStore, set synchronously in loadCopilot), not the last-*resolved* one — otherwise a warm session finishing while a newer workspace's load is still in flight could win the monotonic token and restore its stale workspace over the one being loaded. - The content-search view is keyed by workspace ({#key $workspaceStore}) so a switch remounts ContentSearchInner; late in-flight responses from the previous workspace can no longer land in the new one's component. Backend (EE, via ee-repo-ref bump to 03ef0eb): the free-tier reservation now also prices the worst-case input cap (at the cache-miss rate), and enforce_free_tier_body rejects oversized prompts and pins n=1 — so an aborted large-prompt request can no longer dodge the input bill that reconciliation would otherwise charge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: exclude service accounts from the free AI tier Free-tier eligibility was keyed solely on authed.email. Workspace admins can create and impersonate arbitrary service accounts (synthetic *.sa.wm.dev identities), each of which would receive its own one-time grant — letting one tenant mint many grants and drain the instance-wide daily allowance. Skip the free-tier fallback for *.sa.wm.dev identities. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: activate free AI tier when clearing a workspace provider edit_copilot_config returned AIConfig::default() when the saved workspace config had no providers and no instance config existed; the frontend applies that response immediately, disabling AI even though the free-tier key is available. A later get_copilot_info (on reload) returns the synthetic free-tier config, so clearing a provider behaved inconsistently until reload. Give this response path the same free-tier fallback as get_copilot_info. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: gate the home AI composer behind the global-AI dev flag The "Build with AI" composer starts a session and navigates to /sessions, which lives behind the same wm_dev_global_ai dev gate as the global AI chat. With the gate off (the default), /sessions renders only its gate message, SessionWrapper never mounts, and the queued prompt is silently dropped. Hide the home entry point behind isGlobalAiEnabled() so it isn't exposed before the sessions gate opens. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [ee] chore: bump ee-repo-ref for deepseek-v4-flash price/model fix Points at the EE commit that pins deepseek-v4-flash and its real prices (pico-precision accounting). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [ee] fix: provable byte bound for the free-tier input cap (ee-repo-ref) Bumps ee-repo-ref to the EE commit that caps the raw request body byte length directly (token_count <= byte_count is provable), replacing the unsafe body.len()/2 token estimate that high-entropy prompts could beat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * nit isGlobalAiEnabled * empty commit * fix(frontend): address Codex review on free-tier / home filters - P1: home filters now sync from the URL reactively, so browser Back/Forward updates the chips, kind toggle and results (and clears keys dropped from the URL) instead of leaving them stale until the next filter edit. - Free-tier banner buttons drop deprecated Button props (size/color/border variant) for unifiedSize + a supported variant. - Condense refreshFreeTierUsage comments to a single race-condition constraint beside the guard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): hide empty kind badge on draft-only scripts A draft-only script can carry an empty `kind`, which still isn't 'script' so the row rendered a blue badge whose only content was capitalize('') — an empty pill left of the "Draft only" badge. Guard the badge on a non-empty kind. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(frontend): animate home tree-view group expand/collapse Wrap each owner group's children in ResizeTransitionWrapper so height changes animate. A slide transition only animates the initial mount, but a freshly-opened owner fetches its rows and passes through a transient empty state before they land — the ResizeObserver animates that second growth too. Nested TreeViews inherit the wrapper's context and skip their own, so one observer per top-level owner animates the whole subtree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(frontend): FilterSearchbar boolean auto-set and string-filter presets - A default-false boolean filter has only one useful value, so selecting it sets true immediately instead of opening a true/false picker. A default-true boolean (e.g. "Include library scripts") still shows the picker, where false is the meaningful choice — expressed via a new optional `default` on the schema. - A plain string filter now surfaces any presets targeting it (`<tag>:<value>`) as suggestions once selected, integrated into menuItems so keyboard nav works — previously selecting e.g. "Owner" showed nothing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(frontend): home page toolbar and content-filter revamp - "New" create-menu button (scripts/flows/apps/…) replaces the old Content button; the search bar moves to the right of the toggle group. - Restore the content filter dropped in a merge: a `content` searchbar filter swaps the list for the full-text ContentSearchInner view (EE), aligned flush with -mx-2. - Move the owner/group and label chips off the page into FilterSearchbar presets; ownerFilter/labelFilter now derive from the searchbar keys (data layer unchanged). - Move the list controls (select / tree view / expand-all / sort) inline into the top row between the toggle group and search bar; add margin above the list. - Beta tag on the home AI chat; a bit more bottom margin under it; tighten the gap between the admin/tutorial banners and the list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai): pass the request body to the free-tier reservation Thread the prompt body into resolve_free_tier_credentials so the free tier can size its upfront reservation from the actual request length instead of a fixed worst case (EE c2e248b), fixing normal chats being rejected as "too large". Updates the OSS stub signature and bumps ee-repo-ref. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): gate home Create/Import menu on edit permissions The relocated CreateActionsMenu rendered unconditionally, so operators and users in workspaces protected from direct deployment saw create/import actions they can't use. Restore the original gate (!operator && showEditButtons, the latter from NoDirectDeployAlert). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): address Codex review on filter searchbar - P1: the boolean shortcut now goes through the same tag-insertion path as the normal branch, so it removes the typed search segment instead of leaving it as a stray free-text (_default_) term. - Mark the Runs `show_future_jobs` filter default: true so selecting it opens the picker (false is the meaningful choice) rather than being a no-op. - Home owner/label presets now emit the canonical `key:\ value` form so the applied-preset check matches after a reparse and can't re-offer a duplicate; update the suggestion extraction to strip the leading separator. - Replace deprecated Button props (size/spacingSize/color) on the relocated list controls with unifiedSize. - Fix stale comments: UsageMeter no longer claims a free-tier consumer; the home filter schema comment describes presets, not the removed ListFilters/label badges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): boolean filter shortcut sets value canonically The round-1 shortcut baked `true` into the tag text, which merged into a following tag (e.g. `archived:\ truekind:\ flow`). Instead remove the typed segment, set the value, and reparse so the text is rebuilt canonically — no lingering free-text and no merge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(ai): restate free-tier caller identity contract in the OSS stub; bump ee-repo-ref Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): keep flanking tags separate when boolean shortcut drops a segment Joining `before`/`after` directly fused the tags a removed mid-segment sat between (e.g. `kind:\ flowsummary:\ bar`). Join with a space; reparse then canonicalizes. Also trims the comment to the essential constraint. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(ai): update sqlx cache for free-tier daily-day queries; bump ee-repo-ref The reserve/reconcile daily-usage queries now bind the reservation day (EE change); refresh their offline query cache and point ee-repo-ref at the EE commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai): activate free tier when instance ai_config has no provider An instance ai_config row won precedence just by existing, so an empty {} (valid via global settings / declarative config) suppressed the free-tier fallback and left AI disabled — even though build_copilot_settings_state already treats it as unconfigured. Apply the same has_providers() check to the instance config in the proxy and edit_copilot_config paths. Also refresh the sqlx cache for the reservation ceiling change and bump ee-repo-ref. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(frontend): migrate legacy Home filter URLs to the searchbar keys The old Home UI stored free-text in `search`, owner scope in `filter`, and could write `kind=all`; the generic searchbar sync uses `_default_`, `owner`, and a kind enum without `all`. Rewrite those params once before the sync reads the URL so shared/bookmarked links restore, and drop `kind=all` which would otherwise wedge later filter edits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai): empty instance config in get_copilot_info; label user-disabled Home AI - get_copilot_info returned any existing instance ai_config row before the free-tier fallback, so an empty {} disabled AI in the copilot-info UI even though the proxy now serves the free tier. Apply the same has_providers() gate here. - The Home chat overlay said "No AI provider is configured" when the user had disabled AI in account settings (providers still present). Distinguish that state ("Windmill AI is disabled in your account settings") as the docked chat does, and drop the misleading workspace-config button in that case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(ai): drop redundant proxy service-account check; trim TreeView comment The service-account exclusion now lives in the free-tier helper, so the proxy calls it directly. Also condense the tree-view resize-transition comment to the essential reason. Bumps ee-repo-ref. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(frontend): the Home content filter is not EE-gated ContentSearchInner loads the workspace's scripts/flows/apps/resources and matches their contents client-side, so it works on any instance. Drop the misleading "(EE)" from the filter label and the "EE indexer / off-EE fallback" comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(ee): bump ee-repo-ref for free-tier pricing + exhaustion fixes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): show disabled Home AI overlay statically, not on hover The disabled-state overlay (reason + configure/add-key action) was opacity-0 and pointer-events-none until group-hover, so keyboard and touch users saw an inert composer with no visible remedy. Render it and the composer blur statically when disabled instead. Also bumps ee-repo-ref for the trimmed free-tier comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): give account-disabled Home AI overlay a recovery action The account-disabled branch showed a reason but hid every action, on the mistaken premise that account settings has no linkable route. It opens from the #user-settings hash (the same one the sidebar Account menu uses), so link there. Bumps ee-repo-ref for the free-tier fixes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): gate Home AI composer for operators; a11y and filter-sync fixes - Home composer now uses prefersSessionHandoff($userStore?.operator) instead of isGlobalAiEnabled(): operators reached this route and could submit a prompt into a /sessions page that refuses them, silently dropping it. Also drops the leftover empty header spacer div above the chat. - HomeAIChat: mark the blurred/disabled subtrees inert so keyboard users can't tab into the unreadable textarea (pointer-events-none didn't stop Tab). - ItemsList: keep the role-dependent searchbar keys (include_library, only_user_folders) in the schema unconditionally and toggle `hidden` instead, so useUrlSyncedFilterInstance (which snapshots the key set once) still URL-syncs a key that first appears after a workspace switch. - Bumps ee-repo-ref for the indexer non-parquet build fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): keep CLI/MCP connect row for operators; trim filter comment The previous commit gated all of HomeAIChat behind the operator/session check, which also removed the AI-independent CLI/MCP "Connect workspace" drawer that operators (and the sessions-beta opt-out) had on main. Render HomeAIChat for the same audience as before (isGlobalAiEnabled) and gate only the composer (title, input, examples, overlay) on operator status inside the component; the connect row always shows. Also trims the role-dependent filter-schema comment to the <=4 line rule. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): reconnect Home keyboard navigation to the unified searchbar The searchbar migration replaced the <input id="home-search-input"> the ItemsList keyboard handler keys off, so Arrow/Enter no longer drove the results list. Thread an `id` down to the searchbar's contenteditable (via TaggedTextInput/FilterSearchbar `inputId`) so the handler and the workspace-switch focus restoration find it again; read the caret through the Selection API instead of an <input>'s selectionStart/End; and stand the list's arrows down while the searchbar's suggestion dropdown is open (tracked via onDropdownVisibleChange). In free-text mode the searchbar no longer opens its dropdown on a bare arrow key, so an empty box passes Arrow/Enter to the list as before. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * fix(frontend): stop searchbar Enter inserting a newline; idle typewriter for operators - TaggedTextInput is a single-line filter input, so Enter now preventDefaults the contenteditable's newline insertion (surrounding suggestion-select / list-open handlers still run on bubble). Previously Enter with no row highlighted dropped a literal \n into the query. - HomeAIChat's placeholder typewriter effect now runs only while the composer is shown, so it no longer loops forever driving an unrendered input for operators. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSqa1iRxn9GUE9fegT7bDS * chore: update ee-repo-ref to f2a31156ac08ecb02d89dbc66d72be58e9c877ff This commit updates the EE repository reference after PR #652 was merged in windmill-ee-private. Previous ee-repo-ref: e59b96a2eea5d1110b40c842f17b337ab051bdd3 New ee-repo-ref: f2a31156ac08ecb02d89dbc66d72be58e9c877ff Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
1462f17643 |
feat: rework the evals dataset drawer and run navigation (#10884)
* fix: create eval datasets from the run dialog, not the empty table Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * feat: paginate the evals dialog and show live run progress Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * feat: rework the evals dataset drawer and run navigation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: take the write lock on the empty-state add-a-case action Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: paginate the cases editor and add keyboard page navigation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: wire cases pagination and select runs from the keyboard Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: one highlight for pointer and keyboard, and guard keys on the topmost overlay Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: restore page navigation and answer arrows outside the pages Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: scope eval keyboard navigation to the active topmost surface Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: leave Enter to focused controls and declare topmost from drawers too Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: highlight with the hover surface and open the highlighted run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: restore the run's dataset on the arrow-right fallback Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: keep the chosen comparison when reopening the same run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * docs: state the Modal Enter caveat on EditableTextarea Modal handles Enter at `window` in the capture phase and stops propagation, so inside a dialog the key confirms the dialog rather than committing the edit. The docstring already carried this caveat for Escape; it now covers both keys and names `enterConfirms={false}` as the opt-out. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: render the case result with the chat's prose stack GfmMarkdown falls back to the legacy `!prose-xs` when no `prose` is given, so the case result read differently from a chat answer. Pass `sm`, the stack AssistantMessage renders with, and drop the wrapper whose `text-xs text-secondary` competed with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * refactor: drop the narrating comment on the case result render Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: restore focus to the arriving page on keyboard navigation The restore checked `document.activeElement === document.body` one frame after the page changed, but the inert-driven reset lands after that frame: it read the element the user was about to lose and returned. Ask whether focus was inside the pages before navigating, then focus the arriving page unconditionally, which removes the race rather than re-timing it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: restore focus for any keyboard-driven page change Arming only from the arrow handler missed Enter, which opens the highlighted run from the page itself and never reaches this component. Record whether the last interaction was a key pressed with focus inside the pages — cleared on pointerdown — so any caller-driven keyboard navigation restores focus too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: track focus position rather than the key that navigated Arming from a keydown listener cannot work for Escape: Modal handles it at `window` in the capture phase, registered before this component, and steps a level back from there — Svelte flushes this component's effects inside that handler, before our listener runs. Track whether focus sits in the pages as it moves, so the answer is already settled when the page changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: take focus back when a navigation leaves it nowhere The trail's back button sits outside the pages and is removed as the level it returns from closes, so activating it from the keyboard left focus on a dead element. Claim the arriving page when focus was in the pages, or when it has ended up on the body — never off a control that outlives the navigation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ * fix: type the TextInput binding as the textarea it renders `TextInput` is generic over its underlying element and defaults to `'input'`, so binding the textarea instance to a bare `TextInput` failed svelte-check. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BamH3sRkmn5nP7wo9iKYPJ --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b998267c91 |
fix: show a loading indicator while the initial data table migration is generated (#10900)
Claude-Session: https://claude.ai/code/session_01R3YQT3BShZ3ivp25yQ6Smd Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b57e231c2b |
fix: keep raw-app editor selection consistent across sidebar and tabs (#10885)
* fix: route raw-app editor selection through one switch function Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LPVrfeXbjznotqG7JdF4H * fix: stop announcing folders as selected from the file tree Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LPVrfeXbjznotqG7JdF4H * fix: carry the selection through a folder rename Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LPVrfeXbjznotqG7JdF4H * fix: keep the generated wmill.ts tab out of stale-tab cleanup Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LPVrfeXbjznotqG7JdF4H * refactor: test document existence through one predicate Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LPVrfeXbjznotqG7JdF4H * refactor: route the history replay through the same predicate Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LPVrfeXbjznotqG7JdF4H * fix: clear the selection in the same tick a runnable is deleted Deleting the selected runnable dropped it from `runnables` and left the editor to notice via the stale-tab effect, one frame later. In that window the pane rendered "No runnable at id <key>". The sidebar list now reports the delete instead of mutating `runnables` itself; the editor deletes and closes the tab together, so the selection moves through `select` synchronously. The stale-tab effect stays as the backstop for deletes that come from elsewhere. Also retitle the two sidebar create buttons and rename the FileExplorer exports behind them: both have always anchored on the selected file's parent folder, never the root. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LPVrfeXbjznotqG7JdF4H * refactor: require the runnable delete callback Optional, the row's Delete button renders and does nothing. There is one caller and it always supplies it, so the compiler can hold that. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LPVrfeXbjznotqG7JdF4H --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
815de49e23 |
feat: make the service log retention period an instance setting (#10889)
* feat: make the service log retention period an instance setting Service log retention was a hardcoded 14 days with no override, unlike job retention. It becomes the `service_log_retention_secs` global setting (env `SERVICE_LOG_RETENTION_SECS`, default unchanged at 14 days), reloaded on change like the other retention settings. The constant becomes `DEFAULT_SERVICE_LOG_RETENTION_SECS` and every reader goes through `service_log_retention_secs()`, so the `log_file` sweep, the object-storage orphan scan, the columnar store's compaction and pruning, the retrieval clamp and the search index's trim window all follow the configured value. Loaded outside `initial_load`'s `server_mode` guard: a dedicated indexer trims the search index to a window derived from this value and is not a server. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsnpNSM6K3oyjwntRwJtVN * fix: never let a non-positive service log retention expire every log Every service log cutoff is `now - retention`, so a `0` or negative window puts the cutoff at or after `now` and the next sweep reads the whole history as expired — deleting the `log_file` rows and their object-storage files irreversibly. `0` is reachable two ways now that the window is configurable: it is what an operator types by analogy with the job retention period sitting directly above it, where `0` does mean keep forever; and `SecondsInput` writes a `0` into a field that was merely focused, so saving the Jobs panel is enough. Service logs always have a window, so clamp an unusable value back to the default in the accessor every reader already goes through. The upper bound is where `chrono::Duration::seconds` panics, which would abort the sweep that reads it. The settings field rejects a non-positive value rather than silently correcting it, and its description now names the database rows too — they are swept on every instance, including one with no object storage configured. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsnpNSM6K3oyjwntRwJtVN * fix: address review findings on the service log retention setting - Bound the monitor's `log_file` sweep. Every process rotates a log file a minute, so lowering the retention can make one ordinary setting change expire millions of rows; the unbounded `DELETE ... RETURNING` materialized all of them, and their deletion futures, in a single tick. Batched like the settings-page cleanup on the same table. - Make the retention atomic private and give it one writer, so a value that would expire every service log cannot reach a cutoff by any path, and say so in the log when one is rejected rather than falling back silently. - Cap the retention at a century. The previous ceiling only bounded `TimeDelta` construction, while consumers compute `now - retention`, which panics past year 262143, and build a Postgres interval that overflows well before the old cap. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsnpNSM6K3oyjwntRwJtVN * fix: cap an oversized service log retention instead of shortening it The two unusable directions were landing on the same fallback, so configuring a retention above the ceiling silently produced 14 days — deleting logs the operator had asked to keep for longer. Too large now caps at the maximum, which preserves that intent; only a non-positive value, which would expire everything and has no upward reading, falls back to the default. Also bound the `log_file` drain to ten batches per pass: `monitor_db` runs under a 600s timeout that cancels every maintenance future in the same `join!` and reports a critical error, so a backlog large enough to need batching has to drain across ticks, the way the neighbouring sweeps already do. The settings field carries the upper bound too, and the superseded query's offline entry is dropped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsnpNSM6K3oyjwntRwJtVN * fix: route the new log-file registration cutoff through the retention accessor `send_log_files_to_object_store` arrived on main while this branch was open and reads the retention directly. The atomic behind it is private now, so it goes through the accessor like every other consumer — which also means the cutoff it uses to skip registering already-expired files follows the configured retention rather than a fixed two weeks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsnpNSM6K3oyjwntRwJtVN * fix: say why every mode loads the service log retention setting A worker registers its rotated log files against the retention cutoff, so the comment naming only the indexer no longer covers why the setting sits outside the `server_mode` guard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsnpNSM6K3oyjwntRwJtVN * fix: file service log retention under Monitoring, not Jobs Service logs are the Windmill processes' own logs — every process rotates and registers its own, no job involved — so the Jobs panel was grouping by the shape of the widget rather than by the subject. It sits under Monitoring now, beside the Indexer panel that holds the other service-log window. Its own section rather than inside that panel: the panel is badged EE, while this governs the database sweep that runs on every instance. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsnpNSM6K3oyjwntRwJtVN * chore: update ee-repo-ref to a6e3533b26195918a17fea58646f71d2bbcde288 This commit updates the EE repository reference after PR #752 was merged in windmill-ee-private. Previous ee-repo-ref: 1d93da24bd166b9a5a5cc204034a1d35ffc88474 New ee-repo-ref: a6e3533b26195918a17fea58646f71d2bbcde288 Automated by sync-ee-ref workflow. * feat: say on the service logs page where the logs actually are The retention number alone does not tell an operator what it governs, and the answer differs by instance. Two states are worth calling out because they are the ones where retention does not mean what it looks like: Without instance object storage, each process keeps its files on its own disk. The page lists what every host wrote, since the rows are in the shared database, but can only open the files of the replica serving the request, and a host's files go with it when it is replaced. With object storage but "Delete logs from s3 periodically" off — the backend default, since uploads are gated on a store existing while deletions are gated on that toggle — expiring a log removes the row and the local file and leaves the uploaded copy behind for good. The retention field itself now names every copy it covers and says that full-text search reaches back at most that far, and less when the indexer's own window is shorter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsnpNSM6K3oyjwntRwJtVN * fix: describe raw log files as the transient copy they became Retiring the raw files landed while this was being written: the indexer now deletes each one as soon as it is ingested, and the log viewer rebuilds a file from the columnar store once the raw copy is gone. So the durable copy is the store, and warning that an uploaded file is kept forever when periodic s3 deletion is off only holds where no indexer runs to ingest it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsnpNSM6K3oyjwntRwJtVN * chore: point ee-repo-ref at the EE compile fix EE main does not build on its own: extracting the index-window expression and adding a fourth copy of it landed in separate PRs that never conflicted textually. windmill-ee-private#756 is the one-line fix; this pins it so CI has a tree that compiles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsnpNSM6K3oyjwntRwJtVN --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
7c1a785f75 |
feat: serve service log retrieval from a columnar parquet store (#10886)
* feat: always write service log files as json so they index structured Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ijGGPCFkYhVzisFexAHYx * feat: serve service log retrieval from a columnar parquet store Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ijGGPCFkYhVzisFexAHYx * feat: shrink the service log index to the per-host count it still serves Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ijGGPCFkYhVzisFexAHYx * fix: reclaim the superseded service log index on upgrade Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ijGGPCFkYhVzisFexAHYx * fix: address review findings in the service log store Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ijGGPCFkYhVzisFexAHYx * chore: update ee-repo-ref to ad9e899dfd2ee4e3d18ecf06d016f821968c5a83 This commit updates the EE repository reference after PR #751 was merged in windmill-ee-private. Previous ee-repo-ref: 6ad4064f9d58d83612b42b4ec870384994d64bcb New ee-repo-ref: ad9e899dfd2ee4e3d18ecf06d016f821968c5a83 Automated by sync-ee-ref workflow. * fix: address review nits on the service log store Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ijGGPCFkYhVzisFexAHYx --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
7a0c81d722 |
chore(main): release 1.799.0 (#10874)
* chore(main): release 1.799.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
7dd88c470c |
fix: unify billable seat counting and prevent fork subscriptions (#10818)
* fix: unify billable seat counting and prevent fork subscriptions * fix: authorize candidate before reading its plan, scope seat breakdown * chore: pin ee ref for the stripe checkout fork guard * fix: grant the billable_member view and widen the paid-plan check * refactor: keep the seat rule in rust instead of a view and function * docs: correct the attach guard summary after widening the plan check * revert: keep cloud out of the ci test feature set * chore: update ee-repo-ref to 9ff97cd818e85940fec282c92161e98c1b8583e2 This commit updates the EE repository reference after PR #742 was merged in windmill-ee-private. Previous ee-repo-ref: 0ec0b42565a41f271a45bf24a93467d110c36df3 New ee-repo-ref: 9ff97cd818e85940fec282c92161e98c1b8583e2 Automated by sync-ee-ref workflow. --------- Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
d334831735 |
fix: reject a prefixed error_handler_path on triggers (#10847)
* fix: strip the script/ prefix from trigger error handler paths Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: strip the script/ prefix when collecting trigger handler refs Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: relocate prefixed trigger error handlers on project retarget Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reject a prefixed error_handler_path on triggers instead of resolving it Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: describe error_handler_path as a bare script path in the api schema Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0bbd559ac8 |
feat: instrument AI fill/fix, evals, agents and the debugger (#10853)
* feat: track AI fill, AI fix, evals, reusable agents and debugger usage Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: pin ee ref to the feature_usage registry commit Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: update ee-repo-ref to c3b6f62ea579a3583d4b474e9885c77104cfc87e This commit updates the EE repository reference after PR #745 was merged in windmill-ee-private. Previous ee-repo-ref: 77992910929188a854eadc06ee45971877b6f954 New ee-repo-ref: c3b6f62ea579a3583d4b474e9885c77104cfc87e Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
8f349c032a |
fix: nested template literals in step inputs, and unresolvable $args tags (#10856)
* fix(frontend): keep nested template literals intact in template inputs Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: fail a flow step with an unresolvable $args tag instead of hanging Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): surface input expression errors when running a step test Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): treat an escaped \${ as literal text when escaping backticks Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: accept the string "null" as a tag component, reject only JSON null Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: leave a same_worker step's inert tag alone, log an unresolved flow tag Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): escape every backtick when the template walk desynchronizes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: leave a dedicated runnable's inert step tag alone Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reroute a step only when its own tag is what failed to resolve Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: name the inert-tag guard step_is_pulled_by_tag Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reject a tag only when it interpolates to nothing at all Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): validate the template walk instead of trusting a balanced stack Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: describe what an unresolvable tag actually interpolates to Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(frontend): decide template escaping with a real parser, not a hand-rolled scan Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: name is_flow_step on push now that it is load-bearing Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): heal an expression escaped before nested templates were handled Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reroute a step whose tag reads args that failed to evaluate Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: never hand a job that failed before running to a dedicated runner Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reroute only a step whose args failed, leave other tags untouched Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: drop the post-preprocessor tag fallback, leaving tag resolution untouched Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: leave interpolate_args exactly as it was Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: use a generic example in the template literal tests Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): show an expression escaped by the old rule as it was authored Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): surface input expression errors from every step-run entry point Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: state what is_dedicated_worker actually reads Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): heal only text whose backticks were all escaped by the old rule Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): match the old rule textually so an authored backslash still heals Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): heal only expressions the old rule broke, never ones that parse Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fb82f36e6d |
fix: pre-fill the test panel JSON args editor and align its placeholder (#10871)
* fix: pre-fill the test panel JSON args editor and align its placeholder Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: reseed the JSON args editor when the preprocessor tab is selected Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: seed schema defaults and own-property args in the JSON payload Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: follow the schema in an untouched JSON payload, ignore same-tab clicks Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: latch JSON editor ownership from Monaco, drop the late remounts The pristine check read the bound `code` value, which trails the buffer by SimpleEditor's 200ms debounce — a reseed arriving in that window overwrote text already typed. Latch ownership from Monaco's own change event instead, via a new undebounced `input` event guarded so `setCode`'s `setValue` does not read as an edit. Both `.then(() => argsRender++)` bumps are gone: the arg views now remount at the tab transition only, and follow the schema in through `initialCode` when inference resolves, so a remount can no longer land on an in-progress payload. `FlowPreviewContent.selectInput` overwrote the editor on select but not on deselect, leaving the abandoned input's payload over reverted args. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
90b40fffc3 |
chore(main): release 1.798.1 (#10870)
* chore(main): release 1.798.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
2302e58c24 |
chore(main): release 1.798.0 (#10868)
* chore(main): release 1.798.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
29133398f9 |
feat: a wizard for importing a hub project, and finishing what the import cannot (#10729)
* feat(frontend): guided setup wizard for data tables
On Cloud a data table cannot use the Windmill instance database, so a new
workspace hit a dead end: an alert telling the user to go find a PostgreSQL
resource somewhere else. Setting one up meant three disconnected places, and the
connection could only be tested after the config had already been saved.
Adds a three-step wizard (choose a database -> set it up -> name it) reached from
the data tables settings page:
- Supabase: signs in via the existing supabase_wizard OAuth client and creates
the project from inside Windmill. Because db_pass is an input to project
creation, Windmill sets the password and the user never visits a dashboard.
- Your own database: picks an existing postgresql resource, or adds one with a
connection string through the form that already supports it.
- Windmill database: hands back to the inline row editor, since instance
databases are provisioned by a superadmin.
Verifying access is no longer a step the user takes: Continue runs the check and
passing it is what advances the wizard, so a database that cannot create tables
never reaches the workspace config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: pin ee-repo-ref to the Supabase provisioning endpoints
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): do not claim the database is ready when its check failed
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review findings on the data table wizard
- The Supabase create branch advanced on `provisioning === 4` without consulting
the check it had just run, so a role that cannot create tables could reach
Finish. It now blocks and offers Try again.
- Retrying no longer mints a fresh secret variable + resource each time: the
credentials are only re-created when the password actually changed.
- The generated password is captured before the create call rather than after,
since a throw there can still leave a project behind.
- On a failed provision the project list is refreshed, so the just-created
project can be picked up from the other tab instead of provisioning a second.
- Finish refuses a name that already belongs to another data table, which
previously repointed it at the new database.
- Secrets go to the acting user's namespace instead of a literal `u/admin/`.
- The progress list no longer ticks "Created on Supabase" before the request is
sent, and does not claim the database is ready when its check failed.
- The wizard's resume state is cleared when it closes, so reopening after an
abandoned OAuth round trip is not stuck on step 2.
- The OAuth callback shares the session-storage key rather than repeating it.
- SupabaseConnect uses the shared provisioning helpers instead of a fork.
- Restores the doc comment displaced onto TestDataTableResourceQuery.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): simplify Alert layout and balance its vertical padding
The body was rendered by two near-duplicate branches, each wrapping the text in an
extra div only to hang a margin on it, and the margins disagreed: the collapsible
branch spaced above with mt-2, the static one below with mb-2. Since isCollapsed
defaults to true, every non-collapsible alert took the static branch, so titled
alerts read as 24px of space below the text against 16px above -- visibly
off-centre -- with the title and body flush against each other.
Collapse both branches into one and drop the margins; the container's own padding
now sets top and bottom equally, with a small gap under the title row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): only offer Supabase when its OAuth client is configured
The wizard offered the Supabase card unconditionally, so on an instance whose
superadmin never configured a supabase_wizard client -- or whose backend is built
without the oauth2 feature, which compiles the whole /api/oauth router out -- the
card dead-ended at a 404. Gate it on listOauthConnects, the same check
ApiConnectForm already makes, fetched on open so configuring the client mid-session
does not require a reload.
Also drop the Supabase project ref from the existing-project cards: it is an opaque
identifier that means nothing outside Supabase's own dashboard URLs. Show the region
instead, plus a status word when the project is not healthy, since a paused project
is the one case where the connection check fails for a reason unrelated to the
password.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): run the Supabase OAuth leg in a popup
A full-page redirect unmounts the wizard, so anything the user does on Supabase's
side -- signing in, confirming an email, browsing their dashboard -- leaves them
with nothing pointing back at Windmill, and the wizard had to park its state in
sessionStorage to survive the trip.
Open the connect endpoint in a popup instead. The modal stays on screen throughout
and the callback hands the token back through postMessage rather than navigating.
The parked-state path stays as the fallback for browsers that block the popup.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): scope the connection check to the choice that produced it
A failed check stayed on screen when the user switched Supabase mode or picked a
different provider, so a fresh tab opened showing an error about a database it had
nothing to do with. Clear the report and the error on both switches; re-clicking the
tab already selected leaves an error the user is reading in place.
Also polish the Supabase step: project cards get the provider-card treatment (icon,
p-3, flex column) instead of a hand-rolled variant whose block layout left more
padding above the name than below; form labels settle on text-emphasis; and the
signup link sits under the primary button for anyone who does not have an account
yet.
Drop the "free" badge and the "Free on Supabase" line -- every option in the wizard
is free, so neither told the user anything -- and say what the Supabase card
actually does now that connecting an existing project is the default.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor(frontend): one setup checklist and one Supabase step for every host
The data table wizard, the instance database modal and the resource drawer each had
their own version of the same two interactions, and they had already begun to drift:
the wizard's Supabase resource shape was rebuilt by hand in the drawer, and the
instance checks rendered with no notion of a step being in flight.
SetupChecklist replaces LoggedWizardResult, whose only consumer was the instance
modal. It adds the running state that component lacked, so a list driven by an
endpoint that reports nothing until it returns still shows where it is. Both the
instance checks and the Supabase provisioning stages render through it.
SupabaseProjectStep owns picking or creating a project, and useSupabaseOauth owns
the popup leg. Each host keeps only what is genuinely its own: the wizard saves a
variable and resource then verifies the connection, the resource drawer fills in its
own form. Both trigger authorization themselves, so a host can offer it a screen
earlier than the step does.
The lists load behind a spinner because which mode to open on depends on whether the
account has projects; deciding that after rendering flipped the toggle under the user.
Adds a kitchen_sink playground for the checklist so the animation and every failure
position can be exercised without a backend, a superadmin, or a Supabase account.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): tidy the resource drawer around the Supabase entry point
Connect Supabase was a hand-styled anchor carrying Supabase's brand hex values
rather than a Button, and it sat in a row whose other controls had settled on
unifiedSize md. Making it a Button meant SupabaseIcon had to satisfy IconType, so it
now takes `size` (deriving height/width from it) alongside the string props its other
callers pass.
The manual resource form spaced every field 32px apart and WhitelistIp added another
16px of its own, which read as a gap rather than a rhythm. One gap of 16px, with the
form itself given a little more separation from the description above it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): stop Supabase resources coming up modified when first opened
Resource forms fill in every unset property from the schema as soon as they render,
so a postgresql resource saved without region, root_certificate_pem and use_iam_auth
was dirty -- and had saved a draft -- the first time anyone looked at it. Write them
with the rest of the value.
SupabaseConnect also rebuilt the resource shape by hand instead of using the shared
helper, which is how the pooler host format ended up in two places.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(backend): record where a data table came from and whether setup finished
edit_datatable_config replaces the whole datatables map and DataTable does not deny
unknown fields, so anything the request omits is dropped without a word. origin and
setup_incomplete would have been erased by any unrelated save;
preserve_unmanaged_datatable_fields carries them -- and migrations_enabled, which had
the same problem inline -- forward for entries that already exist, following renames.
setup_incomplete is what lets a row be recorded before the resource it points at
exists, so the wizard can write nothing until the user finishes. There is deliberately
no intermediate state: the setup runs entirely in the browser, so nothing server-side
could advance one.
datatable_health probes every data table at once for the settings page and skips the
incomplete ones, whose resource_path resolves to nothing yet. set_datatable_setup
patches a single entry instead of resending the map. test_datatable_connection_value
checks a connection the caller has not saved anywhere, which the wizard needs before
it has written a resource.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): make destructive default and subtle buttons read red
Both variants were neutral until the pointer arrived, then filled solid red: nothing
marked the button as destructive until you were already on it. They now carry red text
at rest, with a faded red border on default and a light red wash on hover, which is
what the legacy red border style in the same file had always done.
Three call sites passed color="red" alongside a design-system variant. getStyleClass
returns before colour is read for accent, accent-secondary, default and subtle, so the
delete-migration control, its modal confirm and the import-database button had all been
rendering neutral. They pass destructive now.
The dropdown variant strips the button's own border, and matched border-border-light
literally -- a class the destructive style no longer contains.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(frontend): rebuild data table setup around a read-only row
The wizard gathers intent over two steps, reviews it on a third and writes nothing
until Finish, so a billable Supabase project is created only once the user has seen
what will happen. runSetup is also the retry: every step probes for its own result
before doing anything, so running it again on a half-finished data table resumes
instead of duplicating. Its steps are keyed rather than dispatched on their titles,
where rewording one changed what it did.
The settings row stops being an editable form with a dirty/save cycle. It carries the
name, where the database came from, a health dot and two actions; everything rare
moved into the gear panel, which also offers Finish setup for a data table whose
wizard never completed. Manage is ExploreAssetButton, the control the ducklake list
already uses, and the row and panel both link out to the underlying resource.
supabaseResourceValue no longer assembles the pooler host from the region.
aws-0-<region>.pooler.supabase.com is wrong for any project Supabase allocated
elsewhere, so the host, user and port come from the pooler config endpoint.
Two data tables sharing one database also share _wm_migrations, which is probed
unqualified, so the review step warns when the database being connected is already
behind another data table.
SupabaseConnect is deleted. The resource drawer uses the shared project step
restricted to existing projects: creating one is a billed action and belongs in the
wizard, which has somewhere to report what it did. The kitchen_sink checklist
playground goes with it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): fall back to a direct Supabase connection when the pooler cannot be read
Reading a project's Supavisor config needs the database_pooling_config_read scope, which
an instance's Supabase OAuth app may never have been granted. No retry recovers from
that, and the wizard treated it as fatal: the user was left with an error and no way to
finish connecting a project that was otherwise fine.
resolveSupabaseConnection replaces the bare pooler read everywhere it happened. Asking
for session pooling and failing now yields a direct connection plus the reason, which
supabaseResourceValue already knew how to write. Nothing about the fallback is silent --
direct is IPv6-only, which is the whole reason session pooling is the default -- so the
wizard warns on its review step and the resource drawer says so in its toast.
The row is recorded before credentials are saved, so an origin claiming session pooling
has to be corrected once a direct host is what gets written; the run patches it through
set_datatable_setup rather than leaving the panel to report a mode nothing uses.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(frontend): open the database behind a data table, and say when it cannot write
Every database in the list now opens the surface that owns its credentials. A postgres
one opens its resource in the editor drawer; a Windmill instance one opens the instance
modal, which is where its setup checks, password rotation and drop already lived. Both
are reachable from the row and from the panel's provenance list, and the provider icon
moved inside the button so the whole thing is one target.
CustomInstanceDbWizardModal targeted #content unconditionally, which put it underneath
the panel drawer that now opens it. It takes a target, and the panel portals it to the
body.
The status column gains a third state. The probe reports privileges but nothing gated
the dot on them, so a data table whose role cannot create tables showed as Connected and
only failed when someone ran a migration. It reads "Limited permissions" instead, and
opens the panel on the report carrying the GRANTs that fix it -- the settings page has
already probed, so the panel takes that report rather than asking the user to run Test
connection over work already done. fullyPrivileged is exported from the report component
so the dot and the report cannot disagree about what counts as healthy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* revert(frontend): keep the data tables settings table as it was
The settings table and the setup wizard are two changes that only shared a file. Splitting
them makes each reviewable: this branch keeps the wizard, and the read-only row, gear
panel, health probe and clickable databases move to their own branch.
The rows go back to the editable form with its pickers and save footer, still opening the
wizard from Add a database. DataTableSettingsPanel, dataTableHealth and dataTableOrigin
had no other consumers and go with them; the connection report stays, because the wizard
shows it too.
DataTableSettingsType keeps `origin`: the wizard writes it, and the review step reads it
back to warn when two data tables would share one database and therefore one
_wm_migrations table.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): confirm before dismissing the data table wizard mid-setup
Closing was guarded while a run was in flight and unguarded before one, which is backwards:
a run leaves a row to resume from, whereas a backdrop click on the review step threw away
the project, the pasted password and the folder with nothing to recover them from.
Backdrop, Escape and the close button now go through one path that asks first. It only asks
when there is something to lose -- no provider chosen yet, or a run that already produced a
result, closes immediately -- so the dialog does not become something to click through.
Continue in the background still leaves in one click; that exit was always the deliberate
one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): stop the wizard claiming the resource folder controls who can use a data table
"Who can use this database" was wrong. Every path that resolves a datatable:// reference --
both executors and the agent-worker endpoint -- reads the resource unchecked, by workspace
and name. A resource in u/admin is usable by everyone's scripts. The folder governs who can
see and edit the connection, and who can reference the resource directly in a SQL step;
neither is who can use the data table. The wizard was contradicting the tab's own
description two screens later.
The folder select and name field become one Path picker, the same one the resource,
variable and script forms use, so the review step reads as a resource path rather than a
permission choice. Its initialPath is snapshotted when the step opens: Path seeds itself
from it, and a live value fights the typing. Finish now also gates on Path's error, so a
taken or malformed path stops the run before it writes anything.
The button that opens all this says "Add a data table" -- the data table is what you get;
the database is a detail chosen along the way.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* revert(frontend): move the destructive button restyle out of the wizard PR
This reverts
|
||
|
|
52ca19e9ae |
chore(main): release 1.797.0 (#10848)
* chore(main): release 1.797.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
8b80b09f33 |
fix: restrict filesystem workspace storage to debug builds (#10864)
* fix: restrict filesystem workspace storage to debug builds Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q7p2VbtYqaXHGaAskgwVk5 * chore: update ee-repo-ref to b58ad414b098d3d7787001a352bfbb13e43a335f This commit updates the EE repository reference after PR #747 was merged in windmill-ee-private. Previous ee-repo-ref: 1b4dada77a8fe2224579c643550c63b1ac2616de New ee-repo-ref: b58ad414b098d3d7787001a352bfbb13e43a335f Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
c04b570574 |
feat: keep a Hub project live while an update is under review (#10814)
* feat: keep a Hub project live while an update is under review Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * fix: confirm before discarding a Hub update and document the route Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * fix: bind the discard confirmation to the session that opened it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * fix: keep the old wording against a Hub without pending updates Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * fix: say what the review lock actually blocks, in one alert Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * feat: let a publisher cancel a Hub submission from the wizard Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * fix: hide the cancel action on a Hub that cannot withdraw Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * docs: describe startNewDraft for both Hub versions Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * feat: warn when an update carries the published pipeline replay Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * fix: base the stale-replay warning on changed content, not recordings Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * fix: stop the stale-replay warning leaking across updates Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * fix: clear the captured cascade when starting another update Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d * fix: abandon an in-flight cascade when starting another update Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018At6NKGa6cQP1zakMS686d --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e38c449007 |
fix: recover from unresolvable AI session links instead of a dead end (#10854)
* fix(frontend): delete the open AI session by its stable id `session` is a $derived lookup into the session list, so it resolves to undefined as soon as the entry is dropped. Nothing reads it after the removal today, so this is latent rather than a live bug, but the delete handler is async and the id is already available as a prop that stays valid for the whole teardown. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(frontend): recover from unresolvable AI session links instead of a dead end Sessions live only in IndexedDB, keyed in the URL by `session_name`, so a link that resolves in one browser resolves to nothing in another. That hit a dead-end "Session not found" page whose only way out was a button — and it also caught a session of the user's own that had simply never been touched, since an untouched session is never persisted. Redirect instead: land on an empty session (reusing one that already exists, else creating one), replace the URL so back doesn't return to the broken link, and explain the swap in one dismissible notice above the composer. Never land on an existing conversation, which would read as a successful load. The notice explains one arrival, so it is spent the moment the arrival ends: a first message sent, the session deselected, or the page left. Deleting the open session removes it before the handler's own navigation lands — across HTTP when a fork goes with it — so that teardown is gated, otherwise recovery claims the gap and reports the session the user just deleted as missing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
72763c9ba5 |
tighten spacing between login email and password fields (#10811)
Claude-Session: https://claude.ai/code/session_01BmEVHF8afJmgv6saBRYN6w Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
665f83e1f4 |
fix(frontend): operator menu opens on hover, pins on click (#10824)
* fix(frontend): operator menu opens on hover, pins on click The operator hamburger synthesized a trigger click on every mouseenter, so melt toggled the menu: re-entering an open menu closed it, and a real click after a hover-open closed it too. Hover now opens the menu only when closed and closes it 150ms after the pointer leaves; the portaled content carries the same handlers so moving between button and list keeps it open. A click is intercepted in the capture phase: when hover already opened the menu the click is swallowed (melt would otherwise toggle it shut) and pins it instead, so it stays open until a click outside or on the trigger. Opening and closing both go through a synthetic click on the trigger because melt's menubar renders content only when rootActiveTrigger is set, which only the trigger's own click handler does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC * refactor(frontend): move hover-open/pin into Menu's openOnHover prop The hover machinery duplicated what meltComponents/Popover.svelte already offers as openOnHover. Menu.svelte owns both the trigger wrapper and the content div, so the grace timeout, the pin flag and the synthetic trigger click belong there rather than in the consumer. OperatorMenu is back to its original markup plus `openOnHover`, and the other Menubar users can opt in. Popover keeps its own implementation: it is built on createPopover, not the menubar, and does not need the trigger-click detour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC * fix(frontend): show the keyboard highlight on operator menu rows sidebarClasses.hoverBg only reacts to the pointer, so rows styled with it alone stayed transparent while melt moved data-highlighted through them: arrow keys walked the menu invisibly. Affected Home, Runs, Schedules and Tutorials (MenuLink), plus Account settings, Switch theme and All workspaces. MenuLink adds the highlight only when it is rendered as a menu item; the sidebar and settings-menu call sites pass no `item`, so nothing changes there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC * fix(frontend): one highlight state per menu row, accent for the selected one Menu rows carried a hover rule and a data-highlighted rule at once. Melt moves data-highlighted with the pointer as well as the keyboard, so the hover rule was a second, independent state: the row under the pointer and the row the arrow keys had reached both lit up. Menu rows now style data-highlighted only. "More triggers" keeps its hover rule — it is a plain div, not a melt item, so it never receives data-highlighted. The selected row also painted bg-surface-hover, making the current page indistinguishable from a highlight. It now uses the accent pair the rest of the app uses for selection, bg-surface-accent-selected + text-accent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC * fix(frontend): make "More triggers" a real menu item It was a hand-rolled <div role="button" tabindex="0">. Melt collects the rows arrow keys walk with querySelectorAll('[data-melt-menu-id="<menuId>"]'), an attribute only the item builder stamps on, so the row was skipped — and its tabindex was no help either, since Tab inside an open menu is intercepted to close it. It is now a MenuItem. Melt closes the menu on item click unless the click is defaultPrevented, and Svelte delegates onclick to the root, which runs after melt's own listener, so the toggle sits in a capture handler on a wrapper where it reaches the event first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC * Revert "selected sidebar row on the accent tokens" sidebarClasses drives the whole sidebar and SessionPicker, not just the operator menu; restore selectedBg/selectedText to their previous values. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC * feat(frontend): open the operator menu below the hamburger Menu defaults to right-start, which put the operator menu alongside the trigger and over the page header. bottom-start drops it under the hamburger, left-aligned. Set on this menu only; the shared default is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC * feat(frontend): show the operator menu trigger as selected while pinned Nothing distinguished a pinned menu from one that is merely following the pointer, so a click gave no feedback. Menu hands `pinned` to the triggr snippet, and the operator hamburger keeps sidebarClasses.selectedBg while it holds — the tint that hover gives it, now persisting after the pointer leaves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC * refactor(frontend): address Codex/Claude review on the menu hover changes Reuse debounce from $lib/utils for the hover grace period instead of a hand-rolled timer, matching how Popover implements the same delay. Give the "More triggers" capture wrapper role="none" so it doesn't sit between role="menu" and role="menuitem" as an unlabelled node, and spell out in the comment why the listener has to be on an ancestor rather than on the item itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
07c77ead74 |
feat(frontend): flag the fork-compare datatable schema diff as legacy (#10829)
* feat(frontend): flag the fork-compare datatable schema diff as legacy * fix(frontend): make the legacy datatable diff alert copy match the opt-in flag |
||
|
|
78331fda8b |
fix(frontend): key the GitHub App installation selector on installation_id (#10831)
* fix(frontend): key the GitHub App installation selector on installation_id The GitHub Account ID dropdown used `account_id` as both the option value and the lookup key. A workspace can hold several installations for the same org (re-installed, or added from another workspace), so `.find()` resolved to whichever came first: picking the live installation could hand back a stale, token-errored one whose `repositories` are empty, leaving the repository dropdown blank. `RepositorySelector`'s pagination matched the same way and appended the wrong installation's page. Both now key on `installation_id`, and the dropdown appends the installation id to the label only for orgs that appear more than once. Switching installation remounts `RepositorySelector` and clears the selected repository, so its loaded pages no longer carry over. Fixes WIN-2448 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): shorten the duplicate-org installation label Drop the "installation" word from the disambiguating suffix: the id alone already tells the two entries apart, and it keeps the errored variant short enough to read at a glance. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6b73145e72 |
fix(frontend): follow the operating workspace in step input forms (#10834)
* fix(frontend): follow the operating workspace in step input forms Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(frontend): rethrow auth errors and wire remaining variable pickers Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(frontend): use runed watch for picker workspace reloads Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0f3d884c6f |
chore(main): release 1.796.0 (#10810)
* chore(main): release 1.796.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
3b2a6d7604 |
feat(datatables): add a down migration from the migration viewer (#10812)
* feat(datatables): add a down migration from the migration viewer Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ha889ogajX9jbqmwTaF8kD * fix(datatables): refuse an empty down migration and keep the saved one visible Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ha889ogajX9jbqmwTaF8kD * refactor(datatables): use unifiedSize on the new buttons and fix the lock comment Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ha889ogajX9jbqmwTaF8kD * fix(datatables): make the add-down exemption atomic against concurrent additions Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ha889ogajX9jbqmwTaF8kD * fix(datatables): re-test the whole observed row when an upsert skips the lock Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ha889ogajX9jbqmwTaF8kD * fix(datatables): re-test the observed row even when none was read, and sync the down draft on the leading change Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ha889ogajX9jbqmwTaF8kD --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2906504125 |
feat: add instance setting to mute zombie job restart alerts (#10813)
* feat: add instance setting to opt out of zombie job restart alerts Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: preserve explicit false for default-on boolean instance settings Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: invert zombie restart alert setting to a mute flag Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
93081e255f |
fix(frontend): mint string password secrets in the operating workspace (#10815)
* fix(frontend): mint password secrets in the operating workspace A `password: true` string argument is rendered by PasswordArgInput, which mints an ephemeral secret variable on the first keystroke and rebinds the argument to `$var:<path>`. It minted into `$workspaceStore` — the globally active navigation workspace. Session editors operate on a different, possibly forked workspace without switching `$workspaceStore`, and thread that operating workspace explicitly as a `workspace` prop. When the two diverged the secret landed where the user was merely looking while the job ran elsewhere, and the backend failed with `Variable not found`. Add the `workspace` prop to PasswordArgInput and thread it through every hop between a form mount and the minting field, plus the entry points that supply it. Track `mintedIn` so updates target where the variable actually lives, and re-mint when the operating workspace moves after a path already exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(frontend): keep a password field consistent with its argument A parent can replace the whole args object without remounting this field — previewing a saved input, say — leaving `path` and `password` describing a secret the argument no longer points at. Minting from them then copies the old plaintext over the replacement, and the replacement is lost. State that rule once as `argReplaced` and gate every mint on it. The replacement can also land while the create is in flight, so the bound value is captured before the request and re-checked after it resolves; the variable that mint produced was never referenced, so it is deleted outright. A mint that ends without binding re-seeds `password` from what the argument now holds, so the field stops displaying a secret that will not be submitted and a later workspace move cannot re-mint the stale plaintext. `updateValue` returns early before anything is minted, since its 404 retry would otherwise bind over a replacement it cannot see. A failed initial mint now raises a toast rather than passing silently, which also removes the component's last unhandled rejection. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(frontend): guard workspace forwarding to PasswordArgInput Every hop between the form a caller mounts and the PasswordArgInput that mints the secret must forward `workspace`, and so must the entry points that supply it. A hop that drops the prop falls back to the navigation workspace while the top-level case keeps passing, and no typechecker catches it because every hop declares `workspace?: string | undefined`. The forwarded expression is checked rather than the prop's presence, so `workspace={$workspaceStore}` and `workspace={undefined}` fail. Two ways the scan could stop guarding without failing are asserted too: an unterminated mount raises instead of swallowing the rest of the file, and the number of mounts parsed must equal the number of tag occurrences, so a mount written inline rather than at the start of a line fails loudly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(frontend): list and create variables in the operating workspace * fix(frontend): surface and bound a failed recovery mint * test(frontend): end a mount at the first line closing it --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
25a3e6ea7a |
fix(ai-chat): keep the composer usable while a question is pending (#10816)
* fix(ai-chat): keep the composer usable while a question is pending Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL * fix(ai-chat): keep a typed answer when the question's resolver is gone Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL * fix(ai-chat): only advertise the answer affordance on a live question Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL * style: trim the pending-question rationale comments to the 4-line cap Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
29c311ab31 |
fix: qualify foreign key targets in generated datatable migrations (#10821)
* fix: qualify foreign key targets in generated datatable migrations The schema API reports a foreign key's target as a bare table name when it lives in the same schema as the table declaring it. Emitted verbatim that becomes `REFERENCES tickets (id)`, which Postgres resolves against search_path — and a migration's own schema is never on it, so applying it fails with `relation "tickets" does not exist` and the whole transaction rolls back. Nothing is created; the project imports with no tables. qualifyFkTarget resolves the target the way the FK closure does: the declaring table's schema first, then any schema holding that table. The REFERENCES clause is now quoted per part, so a qualified target survives identifiers that need quoting; the constraint name is still built from the unquoted value, so the pg_constraint guard still matches what it creates. `quoteTarget` is opt-in, so alterTable.ts — the only other caller of renderForeignKey — is byte-identical. Reproduced and verified against a real data table: hub.windmill.dev's published helpdesk migration fails as above, and the same SQL with the target qualified creates both tables and the constraint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xg8vXUuHCH3aRkf91sfxjx * fix: resolve bare foreign key targets in the declaring schema Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wq66AbLqo4c5ukWJSc4x4t --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
541b6c8496 |
fix: keep ai chat messages when leaving the page mid-generation (#10809)
* fix: persist ai chat turns mid-generation so leaving the page keeps them * fix: stop chat checkpoints once the turn commits, keep streamed text visible * fix: checkpoint streamed answers as they grow and keep half-run tool batches * fix: checkpoint text as received so a backgrounded tab keeps capturing * fix: keep buffered tool screenshots in mid-batch chat checkpoints * fix: decide committed-text at the flush site, condense checkpoint comments * fix: checkpoint only live streamed text, never text the parser owns * fix: don't swap the chat transcript out from under a running turn * fix: close the pre-loading window in the conversation-switch guard |
||
|
|
9c557859c5 |
feat: AI agent evals: datasets, scored runs and comparison (#10633)
* feat: eval datasets and standalone runs for reusable AI agents Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: agent eval drawer with case editor, runs and capture entry points Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: document AI agent eval datasets and standalone runs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: say how many eval cases the list is not showing Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review findings on eval datasets - keep an edited case's conversation and tool inputs: serde(flatten) silently drops Box<RawValue> fields, so the update payload is spelled out - remount the case editor per case so one case's turns cannot leak into another - require jobs:read / flow_conversations:read on the capture endpoints, which UserDB does not gate by token scope - take the dataset lock in create and update so a delete cannot be undone by a concurrent metadata write, and delete cases before metadata - load more cases beyond the first page, and stop capping the agent picker - record that the version stamp is taken at enqueue, not at resolution Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address round-2 review findings on eval datasets - block operators from dataset and case writes - pass the editor's operating workspace through the drawer and the capture request, instead of assuming the navigation workspace - discard superseded case-list responses so switching datasets cannot land the previous dataset's cases - reject a dataset without a case_id (or vice versa) rather than running an inline case under a dangling association - run unsaved edits inline instead of silently running the stored case - surface the API error body on a failed run - fetch dataset metadata concurrently when listing - $bindable() without a default on the optional open prop - correct the permission and enqueue-time-version wording in the docs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: run an untouched saved case by reference again The editor writes back keys the stored case omits, so comparing the raw objects reported every unedited case as edited: the run went inline and lost the dataset/case stamp its history depends on. Compare a normalized form, and pin it with a test. Also scope the history query to the drawer's workspace and drop superseded responses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: show a dataset's cases as a table, and fix round-4 review findings The case list showed one case at a time with no overview. It is now a table with the case, where it was captured from, and its last run — the last-run column is a single jobs query on the path stamp rather than a request per row. Review fixes in the same file: - keep the edit baseline on the selected case rather than looking it up in the loaded page, so a case beyond page 1 is not treated as unedited and run stale - release the loading state when a superseded case load returns early - reload every loaded page after a write instead of collapsing to page 1 - last remaining 'resolved to' wording in the version tooltip Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: run a dataset as an experiment, with scorers as runnables An experiment runs every case of a dataset against one subject and records the exact case set it executed, so a result set stays reproducible while the dataset keeps changing. Each case runs as its own small flow — the agent, then a step per scorer — so a case keeps the run stamp, history query and trajectory view a single run already has, and scorers need no orchestration of their own. Results are read back per step by node id rather than by walking a nested loop's status. A scorer is any runnable taking (input, output, expected): a script, a flow, or a reusable agent used as a judge. A judge is prompted with the case and the answer as one JSON message; a script or flow receives them as named arguments. Scores accept a bare number, a boolean or {score}. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: results table for an experiment, with scorer columns One row per case: status, the agent's answer, and a column per scorer, with the mean per scorer above the table and a link into each case's run for its trajectory. Averages skip cases a scorer produced no number for — counting a missing score as zero would read as a regression. The drawer's left pane becomes Cases / Results, and Results carries the scorer picker and Run dataset. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: compare an experiment against a baseline Per-scorer deltas on each row and on the mean, and a filter down to the rows that regressed. Rows join by case id, so a case added after the baseline ran has no delta instead of counting as a change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address round-5 review findings on experiments - match scorers by label when diffing two experiments; joining by array position subtracted one scorer from another whenever the scorer sets differed - report a row's status from the case job, not the agent step, so a case whose scorer failed no longer reads as a success - delete a dataset's experiments with it: they hold copies of its cases, and a recreated dataset of the same path would have exposed them - select the experiment that Run dataset just started instead of leaving the table on the previous one - expected is scored now, so stop describing it as having no consumer Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address round-6 review findings on experiments - hold the dataset lock across an experiment launch, so a delete landing between reading the cases and writing the experiment cannot recreate the deleted dataset's inputs - match scorers between experiments on kind and path, not on label: labels default to a path's last segment, so f/a/quality and f/b/quality compared against each other - average mean deltas over the cases both runs scored; comparing each run's own average reported a regression from a case the baseline never ran, with no regressed row to point at - openapi: the row status is the job's, which is also canceled/skipped; runEval takes scorers; the update-case body no longer advertises source, which the handler deliberately ignores - record why the experiment prefix cannot reach a sibling dataset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address round-7 review findings on experiments - release the dataset lock for the push loop and retake it for the write, re-checking the dataset still exists: holding it across the whole launch made every capture and case edit on that dataset 409 until the last job queued - assemble experiment results with bounded concurrency; a 100-case, 3-scorer experiment was 400 sequential lookups, each itself several queries - clear the baseline when it becomes the selected experiment, which was comparing a run against itself and reporting zero deltas - take the header mean over the same cases as its delta while comparing, so the two numbers beside each other describe the same set - a canceled or skipped case is no longer the same grey dot as a running one Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address round-8 review findings on experiments - verify the dataset's identity, not just its existence, before recording an experiment: the path can be deleted and recreated during the push loop, and the experiment holds copies of the old dataset's cases - give the recording lock a longer budget than a case edit, since its jobs are already queued and giving up strands them, and say so when it fails - keep score lookups sequential within a case: nesting two bounded streams multiplied into 32 in-flight queries against a 50-connection pool - clear a baseline that no longer belongs to the loaded experiments, so switching datasets does not leave comparison mode on with nothing to compare - keep a scorer's own mean when the baseline never ran it, instead of blanking a column full of numbers - EvalCaseDraft.expected no longer claims nothing scores it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: do not trust an experiment's job ids, and require write to record one Experiment objects live in workspace object storage, which a script can write directly, and results are read on the unrestricted pool — so a forged experiment naming another flow job returned output the jobs API would have refused. Only jobs this server stamped with that experiment's id are read now. Also from round 9: - recording an experiment requires write on the dataset, not read: it persists into the dataset's namespace and its shared list - clear the results table when the selection changes and surface a failed load, instead of labelling the previous experiment's numbers as the new one's - a storage fault is no longer reported as a deleted dataset - the lock-timeout message at the recording site no longer says to retry, which would run the whole dataset again on top of the jobs already queued - ExperimentRow.status documents canceled and skipped Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: bind the experiment trust check to the requested dataset The previous check matched jobs on the experiment id alone, which the stored object supplies — so copying another dataset's experiment JSON under a readable key carried its jobs' output along with it. A job is now only read if it was stamped for this experiment *and* for the dataset the caller's read access was checked against, and an experiment that names a different dataset is not served from this key at all. Also from round 10: - add the .sqlx entry for that query; without it every SQLX_OFFLINE build failed - serve results over GET: as POST the route-scope middleware classified a read as ai_evals:write, locking read-only tokens out of their own results - clear the selected and baseline experiments synchronously when the dataset changes, so the previous dataset's id is not requested under the new one Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address round-11 review findings on experiments and scorers - give scorers the whole case input, not just the message: an answer that came from attachments or a replayed conversation could not be judged on it - accept a judge's boolean and structured {score} answers, including stringified ones, and pin every documented scorer shape with a test - record an experiment for the cases that did launch when a later push fails, instead of leaving those jobs running with nothing to attribute them to - do not capture a preview parent's synthetic runnable_path as a host flow; the saved case could not be rerun - clear the case table before loading a dataset and surface a failed load, so a failure cannot leave the previous dataset's cases under the new name - keep the results table through a refresh of the same experiment - exclude flow-step jobs from the per-case last-run lookup - drop case sets from the experiment list, which is only used to pick a run - report a database failure at the recording lock as itself, not as contention Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address round-12 review findings on capture and run history - load flow_node.flow for flownode parents: an agent inside a deployed branch or loop captured without its agent, host flow or tool bindings - decide host_flow_path by whether the path resolves to a flow, not by job kind: excluding previews wholesale also dropped the flow editor's step test, whose path is real - page the per-case last-run lookup by created_before until the loaded cases are covered; one page of 200 reported older cases as never run - do not record an experiment when nothing launched - only attach the case input to a job when a scorer will read it - keep the case table through a save; only a different dataset clears it - drop the superseded duplicate comment on the score parser Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop refetching run history on every case write Reading the case list before the first await made the whole job-history query a dependency of it, so every save, delete and Load more refetched up to 1000 job rows and blanked the column. Read untracked instead. - an empty Last run cell now distinguishes never-ran from not-found-within the page bound, which the comment already claimed and the cell did not - reloading a dataset no longer replaces a populated table with a skeleton - keep the score-parser comment that describes every shape it handles Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: keep eval datasets in Postgres instead of object storage Datasets, cases and experiments become rows (`eval_dataset`, `eval_case`, `eval_experiment`, `eval_experiment_case`) rather than objects under a `wmill_eval_datasets/` prefix. What a run produced is still the job's: only case inputs and an experiment's case snapshot are stored. This removes the machinery the object store needed: - The advisory lock and the read-modify-write of a per-dataset JSONL. A case is a row, so there is nothing to serialize. - The launch-time identity check on the dataset. The foreign key makes a concurrent delete fail the transaction instead. - The trust guard on an experiment's job ids, which existed because a script can write workspace object storage directly and could forge an experiment naming somebody else's job. An experiment now chooses every job id and records itself before pushing anything, so a launch that dies partway leaves a recorded case whose job is missing rather than a running job nothing accounts for; cases that never reached the queue are removed again. Row-level security on `eval_dataset` is the authority on who may read or write a dataset, so `extra_perms` grants work and the rule is not mirrored in Rust. Cases and experiments carry a read policy derived from their dataset and no write policy: they are written on the unrestricted pool after the dataset row itself has been asked, with `SELECT ... FOR UPDATE`, whether the caller may write it. Cases are capped at 256 KiB each and 10 000 per dataset, refused rather than truncated. Attachments are S3 references, not inline bytes, so a case that approaches either cap is a mistake rather than a use case. Evals no longer need the `parquet` feature or a configured workspace object storage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * style: align the eval drawer with the design system - Scorer chips are `Badge`s rather than a hand-rolled bordered span, and the section header is a `Label` with its tooltip, as are the case editor's fields (which also gets the label colour right). - The results table showed status as a coloured bullet, which says nothing to a colour-blind reader. It now carries the same icons the runs table uses, with the status as its accessible name. - Feedback colours move to the `-500` shades the brand guidelines name. - The conversation JSON error uses `TextInput`'s `error` prop for the border and the caption style for the message, as elsewhere. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: author an expected answer, tags and attachments on a case Every scorer is handed `(input, output, expected)`, but nothing could produce an `expected` except a conversation capture: the case editor had no field for it and a captured run left it empty. So: - The editor gains Expected, Tags and a read-only list of the attachments a captured case carries. Expected is plain text, or JSON when the answer has structure. - Capturing from an AI agent run keeps what that run answered, which is the only moment a reference answer exists for free. The results table also laid itself out by content, so a long answer pushed the scores — the numbers the table exists for — off the edge of the pane. It is fixed-layout now, with the text columns bounded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: expected is captured from a run and can be authored Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: link a saved agent when inserting an ai agent step "AI Agent" in the step picker was a leaf that always created a blank step, so reusing a saved agent meant inserting a blank one, opening its step input and linking it there. It is a category now, like Flow and AI Sandbox, listing the workspace's `ai_agent` resources next to a blank option, filtered by the picker's own search. A picked agent produces a step that is already linked rather than one linked afterwards: `agent` set, no tools, and only the flow-local `user_message`/`user_attachments` transforms. Seeding the brain keys there would leave transforms a linked step never reads and that `AgentResourceBar` strips on its next link change. Each `on:new` forwarder rebuilds the insert detail field by field instead of spreading it, so a new field is dropped unless the forwarder names it. `agentPath` is typed on both `GraphEventHandlers.insert` and `FlowGraphV2`'s `onInsert` so the next one to forget it fails the check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: restore the link on cancel and simplify the agent bar Cancel on an agent edit forked the step into a standalone copy, which is the opposite of what the word means and needed a paragraph under the card to explain. It discards the edits and re-links the step now, leaving the agent untouched; diverging from an agent is Unlink's job, on the linked card. This flow's `tool_inputs` survive the round trip as overrides, so Cancel no longer folds them into the tools the way Unlink does. Linking a step to a saved agent happens in the step picker at insert time, so the bar's own resource picker is gone and "Save as agent" is the one action left. Its `+` button was a trap besides: it opened the generic resource form, where an agent would have to be written as raw JSON. The card itself was `surface-secondary`, the sections token, so in dark mode it was darker than the pane and read as a sunken well rather than an elevated card. It uses `surface-tertiary` as the brand table prescribes, its tool chips are `Badge`s, and the editing card no longer overflows the pane and clips its own buttons. The remaining tooltip follows the inline `Label` convention rather than sitting in a flex row whose gap stacked on the trigger's own margin. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: rework the AI agent evals surface into one table Evals become a single pane: a dataset of cases, one column per scorer, one row per case, with the run being looked at chosen from the toolbar. Runs are permanent. Running the whole dataset opens one; running a single case records nothing at all — it is a job, and looking at what it did is not a claim that it belongs in the history. Its result and its scores sit over the row until they are saved as a run, which carries the cases that were not rerun and the scoring jobs themselves, so the number that is saved is the number that was looked at. A scorer is a runnable: a judge agent or a script, created in one click and edited in place. Scores carry a reason and per-assertion checks, shown on hover with a rescore button. What ran is always named. A run records the agent version, or — for a configuration that is not deployed — a hash of it, so a table can say that its numbers describe an agent that no longer exists: those rows dim and the table offers to rerun. An agent's draft can be run directly instead of the deployed value, and once those edits are deployed the runs that made them are recognised as that version. A step with no agent of its own is evaluable too, and saving it as an agent moves its history onto it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: keep an agent's in-progress edits on the agent Editing a linked agent forks it into the step, which is what makes the edits runnable there — but the agent is what is being edited, so that is where the unsaved state belongs. The edit is mirrored into the agent's own resource draft as it is made. It then survives leaving the flow, shows the agent as drafted wherever it appears, and is what evals run when asked to run the draft rather than what is deployed. Deploying or cancelling clears it; opening Edit without changing anything does not mark the agent as drafted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: shape the evals surface around a saved agent Evals hang off an `ai_agent` resource, so the surface is now only ever about one: the `draft` subject kind, the standalone-step subject and the move that carried a step's history onto a newly saved agent are gone. - A run is permanent and numbered per agent. Running a single case is a trial: it answers in the panel and never touches the table. - "Run scorers only" opens a run of its own that reuses the answers of the run you are looking at, so a scorer added later measures what already ran without calling the agent again. - A draft run whose configuration is later deployed is stamped, once, to the version it became, so its label stops reading `v23 + edits` forever. - A scorer can carry a pass threshold, read off the scores already recorded. - The table is the case, its answer and one number per scorer; datasets are created and edited in a drawer; a run that executed an earlier state of the current draft says so above the table, in one line. - Which agent a step is, whether it is being edited, and which version it is on is a strip above the step's tabs, because it is true of every tab. - Capturing a case from a step test or a conversation is dropped, and with it the `memory` override on a linked step that nothing set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: run past versions of an agent, and number versions per resource The evals home becomes one table of every run of the agent, whichever dataset each is of, with one badge per scorer. A list spanning datasets cannot hold every dataset's scorers to look a name up, so a score carries its name and kind with its number, and thresholds are joined in per run and column. Run now asks what to run: the latest agent, resolved when the run executes as a flow step does, any past version, or the unsaved edits. Pinning is a subject kind of its own, since a linked step resolves the resource live and inlining is the only way to run a version that is no longer current. Scorers move into the edit-dataset drawer. The column header over a run reports and nothing else: a run is permanent, and a control there that changed the columns would edit the past from the one place that must not. Adding one offers four ways rather than two, writing and reusing being different jobs, and both new kinds open with a summary filled in. Versions are numbered per resource. `resource_version.id` is one identity sequence for the whole table, so an agent saved nine times read v4 ... v24, and the gaps counted writes in workspaces the reader cannot see. The id stays how a version is addressed; the new number is what it is called, in the resource history drawer as well as here. It is assigned on write rather than counted on read because trimming past the cap and clearing a history both take the oldest rows, and counting the survivors would renumber a version a run already names. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: read the dataset a remembered selection names Reopening the evals modal restored the last dataset from storage as a bare path, without reading the row it names. Every "is this already the one?" test compared against that selection, so all of them short-circuited and the dataset was never loaded: editing it opened a drawer with no summary, no scorers and no cases. The remembered path is now brought into context the same way any other choice is, and the tests compare against the dataset that is loaded rather than the one that is selected, so a selection can no longer stand for a read that did not happen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: give dialogs a trail in their header A dialog deep enough to navigate had nowhere to say where you were: the header held a fixed title, and the way back was a control each body placed for itself, somewhere in a toolbar that moves with everything else the toolbar holds. The header is the one part of the surface that does not move, which is where the trail belongs. `Modal` takes an optional `trail` of levels below its title, rendered as a breadcrumb whose ancestors are the way back. Declarative on purpose: callers of this depth already hold the state that says where they are, so the dialog reads it rather than owning a stack they would have to push and pop in step with it. Escape follows the trail. Leaving a level is what someone deep in a dialog means by it, and closing the whole surface throws away the navigating they did to get there; at the root it closes as before. That only works if a dialog can tell it is the surface being addressed, so `Disposable` now answers `isTopmost()` and the dialog asks before acting: it keeps Escape for itself, so nothing else was arbitrating between it and a drawer opened from inside it, and both were acting on one key press. Evals is the first caller: its runs list is the root, a run is a level in it, and the back button that used to sit above the table is gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: portal dialogs out of wherever they were opened from A dialog rendered in place inherits whatever the calling component happens to sit inside. One `transform`, `filter` or `overflow` anywhere above it makes its `fixed` positioning resolve against that ancestor instead of the viewport, and a surface meant to cover the app is then confined to a box it never asked for: the nav rail paints over it and its own edges are clipped. Drawers have always portalled for this reason. Dialogs only did so when an enclosing pane claimed them, and rendered in place otherwise, so the same screen could show a drawer over everything and a dialog trapped behind the nav. They now portal the same way: to the pane when one claims it, to `body` otherwise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: make the dialog's title the first step of its trail The trail listed levels below the title, so a dialog one level deep read "Evals > All runs > Run 20 · v6": three steps for two places, the first two of them the same place under different names. The title is the root, so it is the root's own segment, and the trail a dialog is given is now the whole path with that segment at its head. Its height stopped moving too. A heading carries a line-height of its own, so a header holding only an h3 stood six pixels shorter than one holding segments as well, and the dialog's whole top edge stepped as you navigated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: sharpen the evals controls around where you are standing Each screen now offers what belongs to it. The list starts runs; a run is a record, so it offers only the one thing that acts on the record itself, which is measuring the answers it already stored. Starting a fresh run from inside one asked which agent and which dataset from the screen least about either, and scoring an existing run was offered from the list, where there is no run to score. Which run and what it is read against are one question asked twice, so they sit together rather than at opposite ends of a row. Choosing what to run is now a toggle over the two states worth naming, the draft and the saved agent, with every earlier version one click further: running an old version is deliberate, and a list made all three look alike. The draft is read when the dialog opens rather than taken from the caller's polled copy, which could be seconds behind an agent edited a moment ago and would leave the option out exactly when it is the reason for opening the dialog. The dataset field carries its path under it and its edit button on hover, as a resource picker does, so the closed field says what the open list said. Edits waiting on an agent are a "draft" here as everywhere else in Windmill, rather than "+ edits". The dialog runs an evaluation rather than "the agent", which is what it was already called everywhere it is recorded. An agent being edited keeps its evals button on a line of its own, clear of the decision to save or discard. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: settle the evals controls on the patterns Windmill already has The version choice uses ToggleButtonMore, as the AI provider picker does: the two states worth naming stay in the group, the rest are behind the overflow menu, and the one you pick joins the group rather than appearing in a second control below it. The deployed one says which version it resolves to. A run offers nothing to start. Scoring an existing run again was the last thing left there, and it was one button explaining a distinction that the run and the dataset already make between them. The warning that a run executed an earlier draft is about the run on screen, so it goes when the run does rather than following you back to the list, and it sits against the table instead of inside a frame of its own. A dataset just created stays open for its scorers and cases: those are what a dataset is, they can only be added to one that exists, and closing on create sent you to find it again to add them. Scorer settings are a cog rather than a word, now that the row holds three actions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: close the gap in the version toggle and say what naming a dataset does The overflow trigger is not a pill, so the room it reserves showed as a gap between it and the button before it; it is pulled in by that much. The dataset field gets its clear button, which is also the slot the edit button is positioned against, so the two now sit where a resource picker puts them. Naming a new dataset said nothing about what happens next, and the drawer looked like it was missing the rest of itself. It says so instead: a scorer and a case both belong to a dataset, so there is nothing to attach either to until this one exists, and creating it leaves the drawer open on them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: choose a dataset's scorers while naming it A scorer is a reference to a runnable, not a child of the dataset, so it needs the dataset's name but not its row. The list is collected in the drawer while the dataset is being named and sent with the create, which already accepts one, so a dataset arrives holding the columns that were chosen for it rather than being made empty and then edited to hold them. Cases stay where they were: a case *is* a row of the dataset, so there is nothing for it to be a row of until one exists. The drawer says which of the two is which instead of leaving the screen looking like it is missing the rest of itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: level the version toggle and name the dataset in its own field The overflow trigger stands a row taller than a toggle button, so the group grew to its height and left the sunken background showing under every pill beside it. Every child of the group is the same height now, which is why the AI provider picker never had the band: it sizes them all alike. The dataset field says the summary with the path after it rather than carrying the path on a line below. The list stacks the two, which a one-line field cannot do, so it says both the other way round. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: tidy the evals forms and the run's own controls Picking a scorer that exists chooses between two sources rather than showing both: the ones already measuring something, and everything else in the workspace. The first list says what each is called with its path under it and what it already measures on the right, instead of three columns that were the same path truncated three ways whenever a scorer had no name of its own. A dataset's drawer says what it is for on the page rather than under an icon, and its summary is sized like the field beneath it. The run's own row lines up with the table under it, the warning above that table is spaced off the rule rather than sitting on it, and adding a case is gone from a run: a run is a record of cases that were answered, so curating them from it is editing what it measured. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: create a dataset holding the cases written for it Creating a dataset takes the cases to create it with, so one can be assembled in a single act instead of made empty and then filled in. The drawer holds them while the dataset is being named, gives them ids of its own to be edited by, and sends them with the create. Every case is checked before the dataset is written. `eval_case` grants users no write, so the rows cannot be inserted in the transaction that creates the dataset under the caller's own policies; validating first is what keeps "created holding these cases" from becoming "created, holding some of them", and the rows that do follow go in one transaction of their own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: name the button for what it opens, and say what each version is Starting an evaluation asks which state of the agent and which dataset, and both cost a provider bill, so a button that read as spending one on the way past was lying about the click. It opens something, and says so. Running one case from the panel keeps its own name and its play icon, because that one does run on click. The version options say what they are rather than what they are not: what a flow step would or would not run is a fact about somewhere else, and someone choosing what to evaluate is not standing in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: give the editing card two rows and mark evals as beta At the width of a step panel the card's one row wrapped: the line naming the agent, the line saying what saving does, and the two buttons deciding the edits' fate all fought for it. Deciding gets a row of its own, and evals sits against the line it is about, since evals of an agent being edited run the edits. Evals is named wherever it is offered. It read as a word in one state of the card and as an icon in the other, which is two things to recognise for one door. The dialog carries a beta badge against its own name, before any level below it: every way in lands there, so it is said once and stays put as you navigate. The version toggle spells out which is which. Both are the agent at v2 and the difference between them is the whole choice, so it is worth the width. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: name a new dataset, and lay the scorer's settings out like a step's inputs A new dataset arrives called "Dataset 1", which the path follows as it follows any summary: a dataset with none was one every table could only call by its path, and the two seeds are what the summary rule already produces. Scorer settings put each field's description between its label and its input, where a step's inputs put theirs, and its inputs are the size the rest of the drawer uses. The runnable behind the column is a link to it with its kind's icon, since it is a resource of its own and the one thing about it these fields cannot change. The line explaining that a pass line re-reads recorded scores went: the threshold is a number to set, and how it is applied is not a decision being made here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: curate a dataset in the drawer and save it in one act The drawer holds the cases while they are edited and writes them when it is saved: added, changed and dropped, whichever it is. Typing no longer writes, so a set is never half saved while someone is still deciding what is in it, and Save means the same thing whether the dataset exists yet or not. A case panel offers reading rather than acting. Running one case now and editing one from a run were the last two ways to change a record from the screen showing it, and the machinery behind the first went with it. The answer is rendered as the prose it is, under what it is: the case's result, whichever run is selected above it. The rest is what the run's table was doing to its own edges: a column name is clipped to its column rather than running into the next, the table squares off against an open panel, and that panel closes with the run it belonged to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: one border above a table, and a link to the run's job The row above the table drew a bottom border and the table draws its own top edge, so every table sat under two lines. The row keeps its spacing and the table keeps its edge. A column header no longer spins while its scores arrive: the cells under it are where the numbers are missing, and they say so themselves. The beta badge is the height of the word beside it rather than of the line it sits on. A run is one flow and therefore one job, so the run says where that job is: what it is doing, what it cost and what it logged are all there rather than reconstructed from the table. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: stream scores as each scorer finishes, and show them per case A scorer runs after the agent inside the case's own iteration, so its verdict can be read as soon as its step is done. Waiting for the iteration to end held every column of a case back until the last of them finished, which is why answers arrived one at a time and scores all at once. Reading a job that is still running needs one guard: a module with nothing in it is a step that has not run, not one that produced nothing, and recording the second makes a failure that never goes away. The panel beside the table shows what each column made of the case and why. The reason a judge gave was stored and never shown, which is the half of a score that says anything. It stops repeating the question the header already asks, and a case still running reads as waiting rather than as an answer that says "Running". A run is a number beside a dataset, so the list puts the two together. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: score a case with every scorer at once The scorers of a case read the answer and never each other, so they ran one after another for no reason: measuring a case now takes as long as its slowest column rather than as long as all of them. Each is a branch of its own, kept from failing the others, so a judge that errors costs its own column and no more. An iteration is three steps again — answer, payload, scores — rather than one per scorer, and each branch is named for the column it produces, so the graph of a run says which scorer did what instead of spelling out an id. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: read a judge's score out of the JSON it nearly wrote A judge quoting the agent inside its own reason writes those quotes unescaped, which is invalid JSON and also the most ordinary sentence for it to produce. The whole verdict was being thrown away over it, so a column that had a number reported having none. The number and the reason are now read straight out of such text. Deliberately not a second JSON parser: it finds the two keys and takes what follows, which is what survives a quote in the middle of a sentence. A case still running says so with a spinner rather than with the word "Running" sitting where its answer goes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: ask a judge for a shape instead of trusting it to write one A new judge carries an output schema, so the provider holds it to `{score, reason}` rather than the prompt asking it to. Windmill already delivers a schema whichever way the model takes it, a tool for Claude and Bedrock and the native parameter elsewhere, so there is no list of models to keep here. An agent with no runs offers its first one where the first row would be, rather than from a toolbar above a table that has nothing in it. Starting a run no longer picks a dataset for you. It fell back to whichever came first, which on an agent that has never run means offering another agent's set as though it were the obvious one; and with no dataset at all it says so and offers the one move there is. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: report a column that failed throughout, and hold the run dialog The runs overview dropped any column that produced no number, so a judge that failed on every case of a run vanished from the row and read as a column nobody had asked for. The aggregate now reports every column that has cells, with the count of the ones it failed on, and the badge says "failed" where there is nothing to average. A column with no cells at all is still left out: that one was added after the run and has nothing to say about it. Creating a dataset closes the drawer rather than turning it into an edit of what it just made: scorers and cases already ship with the create, so there is nothing left to stay open for. Reached from the run dialog, it gives the screen back with the new dataset selected, and the dialog keeps the version you had already chosen. Also: - the case panel's job link moves to the panel's own header, where its scope is: the job is the whole iteration, not the answer it sat over - one action in the scorer drawer's header, as its neighbours have. The reuse list picks rather than adds, and says which dataset each column already measures - adding a case is the last row of the list it lands in - the pane shows what it has read rather than an empty state it has not earned yet, and its rows say they open - the linked agent card loses a border it had inside another one * fix: keep the linked agent card's outline The card is a thing inside the step's inputs rather than a section of them, and the outline is what says so. Only the rule inside it goes: the detail it separates is already set apart by being detail. * refactor: fit the eval surface to the shipped design * feat: give a nested dialog a back control and the runs list its own moves Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: put a dialog's description under its title Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: fold a dialog's back control into the crumb it returns to Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: edit a dataset's cases as a table rather than a list beside a form Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: edit a dataset's cases in the grid the data tables are edited in Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: edit a grid cell of prose in place, and cap a dataset at one page Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: keep the cell editor's styles beside it, not in the vendored theme Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: keep an empty cell empty and cap the editor's growth Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: name the step that assembles a run for the scorers Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: run the payload step natively, and say so when nothing serves that tag Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: report an answer as answered while its scorers are still running Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: let a scorer say a case is not one it measures Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: score the answer, and leave a case with no expected answer unmeasured Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: split the evals backend into modules Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: record what a run produced so it outlives its jobs * fix: read only the agent step's own tool jobs into the payload * fix: pin a run's configuration and give the judge the attachments * feat: write a dataset's cases in one transaction * chore: refresh the sqlx cache for the eval queries * fix: drop results a newer selection has superseded * fix: keep a draft the agent editor never opened on * feat: let a run record what it produced instead of waiting to be read * fix: serialize the replacements of a dataset's cases * fix: stop the poller from superseding a read slower than its interval * chore: refresh the sqlx cache * fix: keep a failed read from settling a cell as a case with no answer * fix: hold the case grid while its save is in flight * fix: keep a failed collect step from failing the run it recorded * chore: refresh the sqlx cache * fix: commit an open cell into the save that reads it * refactor: size the eval buttons with unifiedSize * docs: describe a run as the one flow it is * fix: show a run's recorded rows when part of it cannot be collected * refactor: size the remaining PR-added buttons with unifiedSize * fix: save the dataset name that was submitted, not the one typed after * fix: force an open cell into the save that was pressed for it * fix: refuse to score a run whose evidence could not be read * fix: hold one lock over a dataset's case count and its writes * fix: keep one unreadable run from costing the whole runs list * refactor: drop the banned bindable-default from the eval props * fix: hold the scorer controls while the dataset is written * fix: read only the caller's own draft of an agent * docs: say in the contract that a run pins its configuration * fix: say a scorer did not run rather than blaming a missing answer * feat: resume the agent draft you already had when you press Edit * refactor: build the trail and dataset controls from Button * fix: clear the open-cell flag when the drawer reopens * chore: refresh the sqlx cache * fix: read a run's configuration and its version from one snapshot * fix: refuse a dataset path or summary the column cannot hold * refactor: handle the agent draft the way the resource editor does * fix: run only a configuration the launch actually read * docs: bound dataset path and summary where they are submitted * fix: surface a stalled agent draft instead of claiming it is kept * fix: stop claiming a draft holds edits a failed write never sent * fix: word a missing score only once the run says whether the case answered * fix: let a breadcrumb crumb shrink so its truncation applies * docs: describe where an agent's unsaved edits live and what drops them * fix: keep harvesting scores when the run cannot yet word a missing one * fix: report a refused draft write the card was reading as a save * fix: drop the refused draft write when the server copy is taken instead * refactor: build the scorer and dataset pickers from the design system * fix: say what removing a scorer column actually does * fix: drop a refused draft write wherever the server copy is read * fix: let a picker row be as tall as the two lines it holds * docs: record what removing a scorer column does to recorded runs * fix: send a queued draft write before reopening, and drop only what it refuses * refactor: write the agent draft at commit points instead of mirroring keystrokes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: run an agent's edits from the step instead of keeping them as a draft Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: make the diff badge keyboard operable and refuse an edits run without its edits Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: drop the dataset icon from the scorer picker rows Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: size the evals buttons like the rest of windmill and call a run of edits edits Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: count a brain expression as an edit of the linked agent * fix: cap scorers per dataset and report a launched run as launched * fix: harvest scores in one read, refuse duplicate case ids, allow group paths * fix: mint scorer ids server-side, save a dataset edit in one request, check attachments * fix: write a dataset edit and its cases in one transaction * fix: atomic dataset create/edit, reset eval pane per agent, stable pending scorer ids * refactor: govern eval_case writes by RLS so a dataset edit is one transaction * fix: pin launch snapshot, order case locks, cap dataset size, guard stale load * fix: cap dataset bytes on single-case writes, reset run-dialog flag on load failure * feat: migrate eval datasets on username change, settle unspawned cases, drop unused case endpoints * fix: resolve scorer scripts as the caller and pin their hash; migrate scorer paths on rename * fix: bound a failed tool call's error to the payload truncation cap * fix: pin scorer hash as a hex string, reject missing judges, migrate eval authorship * fix: record an out-of-range scorer result as an error, not a score * fix: resolve judges in one caller-scoped read, pin deployed scripts, bound pass_if * fix: settle unspawned cases only when the run completes, and their score cells too * feat: reassign eval datasets and their path references when offboarding a user * fix: use the regex backreference in offboarding eval path rewrites * fix: register eval datasets in offboarding registries, keep resource-version param name * refactor: name the resource-version path param id, since it is the row id not the version * fix: validate dataset paths canonically, clone eval data on fork, surface eval load and launch failures * docs: note MCP tool results are not yet surfaced to eval scorers * fix: show the eval error state on any load failure, not only an empty dataset list * fix: preserve eval case order across a batched save Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjewUvXFEjNLLw7kxz41h * docs: scope the eval launch delete-safety guarantee to the assembly window Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjewUvXFEjNLLw7kxz41h * fix: only offer deployed scripts as eval scorers, drop unbuilt rescore claim Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjewUvXFEjNLLw7kxz41h * fix: enforce 0-1 scorer threshold in the settings drawer and clear stale eval load errors Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjewUvXFEjNLLw7kxz41h * fix: scope subject version/hash reads to the caller and keep a 0 pass threshold Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjewUvXFEjNLLw7kxz41h * fix: select the saved dataset when creating or renaming from the Run dialog Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjewUvXFEjNLLw7kxz41h * fix: gate eval dataset rename on path ownership, not just write access Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjewUvXFEjNLLw7kxz41h * fix: tolerate a malformed agent config when resolving the deployed label Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjewUvXFEjNLLw7kxz41h * refactor: trim eval code and comments, fix shared select and modal paths Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: drop the rename warning when editing an eval dataset path Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: add eval dataset delete, keep summary on partial edits, settle resultless scorer cells Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: cover parseThreshold and subjectLabel Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: hold dataset Save during a scorer write, derive draft_hash only from the carried draft Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b6e059116a |
feat: track token cost in AI sessions and chats (#10688)
* feat: track token cost in AI sessions and chats * fix: address review findings on AI cost tracking * fix: price inherited and overridden models at their real rates * fix: stop newer model revisions inheriting an older price * fix: stop a sub-model inheriting its family's price * fix: keep alias suffixes resolving to their model's price * fix: count OpenRouter cache writes and drop unverifiable rates * refactor: move AI spend out of the chat into workspace and user settings * fix: pin the usage workspace per turn and stop inventing cache rates * fix: leave Sonnet 5 unpriced while its promotional rate runs * docs: record the new table in the schema summary and tighten comments * fix: mark estimated AI costs with ~ and drop session grouping Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: name the workspace in the self-scoped AI usage title Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: state that overrides never replace a provider-returned cost Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: let a cleared cache rate inherit again and flag partial totals Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: clear a refused rate's error when the input snaps back Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop a revision variant inheriting its base family's rate Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: report AI usage before tools run and price self usage consistently Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: key pricing rows on the model id usage is reported under Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: surface Bedrock and Gemini usage the chat proxy was dropping Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: count Gemini tool-use prompt tokens as input Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: price flat-rate Gemini Flash models Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: state the tool-use token invariant once Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
7751d3e43e |
feat(frontend): warn when COEP blocks cross-origin resources in raw app editor preview (#10328)
* feat(frontend): warn when COEP blocks cross-origin resources in raw app editor preview Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gr3udvsSKYyH6mnDvGDqEE * fix(frontend): hedge COEP toast wording and attach warning on detached preview initial load Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gr3udvsSKYyH6mnDvGDqEE * chore(frontend): condense COEP warning rationale comment Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gr3udvsSKYyH6mnDvGDqEE --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
74af4ed939 |
chore(main): release 1.795.0 (#10807)
* chore(main): release 1.795.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
25d9a20630 |
fix: require an unscoped token to reach the workspace encryption key (#10798)
* fix: require an unscoped token to read the workspace encryption key Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: hold the encryption key's write path to the same token bar Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: audit a workspace export only once nothing can still reject it Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: carry the new audit operation into the served openapi spec Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
01fc4f1568 |
fix: name the requested storage when a workspace storage lookup finds nothing (#10803)
* fix: name the requested storage when a workspace storage lookup finds nothing * chore: point ee-repo-ref at the merged ee commit |
||
|
|
a350f7c68e |
feat: show the date on the runs dashboard chart axes (#10808)
* test: assert the unpacked repo symlink without following it `unpack_keeps_a_link_that_stays_in_the_repo` read through the link it had just unpacked. Windows stores a symlink's target verbatim and its object manager rejects the `/` in a POSIX one, so `read_to_string` came back with `ERROR_INVALID_NAME` and the release's `cargo_test_windows` job was red. Pin what the function is responsible for on every platform — the link is kept and materialized — and read through it only where a POSIX relative target resolves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx * test: key the cli sync-map fixtures with the platform separator A sync map is keyed with the platform separator on both sides — `FSFSElement` walks the tree with `path.join`, and the remote `ZipFSElement` starts at `"." + SEP` and joins from there — while an `!inline` reference is always forward-slash. `lock_dedup.ts` follows that convention; the fixtures did not, so on Windows they built a map shape the CLI never produces and 12 of them failed. `getTypeStrFromPath` is the same story: it matches `"dependencies" + SEP`, and the test handed it a forward-slashed path. Build the fixture keys through the separator, leaving the `!inline` references and the `present` map forward-slash, as `sync.ts` hands them over. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx * ci: skip the discord comment relay when the thread lookup returns none A rate-limited or unauthorized Discord response carries no thread list, and under `bash -e` that aborted the step — jq cannot iterate null, nor parse the HTML error page Cloudflare answers a 429 with — before it reached the "thread not found, skipping" branch right below. Three comment relays failed that way on the 1.794.0 head. Keep the step green for both, but tell them apart: a response with no thread list is a delivery that was dropped for a reason worth seeing, so it warns with the body it got, while a PR that genuinely has no thread stays quiet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx * feat: show the date on the runs dashboard chart axes Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019UA8Kikr1QD28g2fyWoSbj * fix: keep the runs chart date visible on sub-day ranges Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019UA8Kikr1QD28g2fyWoSbj --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
1a506b8f22 |
chore(main): release 1.794.1 (#10801)
* chore(main): release 1.794.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> |
||
|
|
dc0df45c81 |
chore: bump git-sync hub scripts to windmill-cli 1.794.0 (#10802)
* test: assert the unpacked repo symlink without following it `unpack_keeps_a_link_that_stays_in_the_repo` read through the link it had just unpacked. Windows stores a symlink's target verbatim and its object manager rejects the `/` in a POSIX one, so `read_to_string` came back with `ERROR_INVALID_NAME` and the release's `cargo_test_windows` job was red. Pin what the function is responsible for on every platform — the link is kept and materialized — and read through it only where a POSIX relative target resolves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx * test: key the cli sync-map fixtures with the platform separator A sync map is keyed with the platform separator on both sides — `FSFSElement` walks the tree with `path.join`, and the remote `ZipFSElement` starts at `"." + SEP` and joins from there — while an `!inline` reference is always forward-slash. `lock_dedup.ts` follows that convention; the fixtures did not, so on Windows they built a map shape the CLI never produces and 12 of them failed. `getTypeStrFromPath` is the same story: it matches `"dependencies" + SEP`, and the test handed it a forward-slashed path. Build the fixture keys through the separator, leaving the `!inline` references and the `present` map forward-slash, as `sync.ts` hands them over. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx * ci: skip the discord comment relay when the thread lookup returns none A rate-limited or unauthorized Discord response carries no thread list, and under `bash -e` that aborted the step — jq cannot iterate null, nor parse the HTML error page Cloudflare answers a 429 with — before it reached the "thread not found, skipping" branch right below. Three comment relays failed that way on the 1.794.0 head. Keep the step green for both, but tell them apart: a response with no thread list is a delivery that was dropped for a reason worth seeing, so it warns with the body it got, while a PR that genuinely has no thread stays quiet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx * chore: bump git-sync hub scripts to windmill-cli 1.794.0 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |