mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 08:01:35 +00:00
script-editor-edit-code-debug
13594 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
00392ba548 | fix | ||
|
|
ba4b368706 |
fix: prevent variable push from corrupting is_secret variables (#9705)
* fix: prevent variable push from corrupting is_secret variables Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(cli): unit-test looksLikeWorkspaceCiphertext shape detection Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): scope is_secret downgrade to single-file push, not sync push Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): warn when variable push stores a secret value as already-encrypted Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): route workspace-resolution and auth diagnostics to stderr Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(cli): rephrase comments to describe current behavior, not history Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
723a65920f |
chore(main): release 1.737.0 (#9728)
* chore(main): release 1.737.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.737.0 |
||
|
|
c644311eca |
fix(ext-jwt): reject external JWT auth for non-existent workspaces (#9723)
* fix(ext-jwt): reject external JWT auth for non-existent workspaces External JWTs are validated (not generated) on our side and never revoked by us. The usage-tracking upsert into unique_ext_jwt_token ran unconditionally, so a token carrying a workspace_id whose workspace no longer exists kept refreshing its row on every presentation — surfacing as a "new token" in the superadmin external-JWT view. Gate jwt_ext_auth on the requested workspace existing (EE companion). When it does not, auth fails (token is unusable) and no usage row is written. The check is existence-only and intentionally ignores the soft-delete flag, so deleted-then-restored workspaces keep working. Bumps ee-repo-ref.txt to the EE companion commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(ext-jwt): cache workspace-existence lookups in jwt_ext_auth Bumps ee-repo-ref.txt to the EE companion commit that caches the workspace-existence check added in the previous commit, so a token aimed at a missing workspace no longer hits the DB on every request (auth failures aren't cached upstream). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to ac1f6f666f36141cb6ba6f8eaa614821a90464ad This commit updates the EE repository reference after PR #626 was merged in windmill-ee-private. Previous ee-repo-ref: e23fa03ec16909c127e8ecf0855595911c29512d New ee-repo-ref: ac1f6f666f36141cb6ba6f8eaa614821a90464ad 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> |
||
|
|
31d9215e5a |
fix: bound orphan-cleanup drain rate with capped multi-batch loop (#9730)
Follow-up to #9727. The orphan cleanups (cleanup_job_perms_orphaned and cleanup_job_result_stream_orphaned_jobs) deleted at most one 100k batch per monitor iteration. Each statement stays short and lock-light, but a single batch per ~30s cycle caps the drain rate at ~100k/30s, so a large one-time backlog (tens of millions of rows) takes ~hours to clear. Loop the batched delete up to ORPHAN_CLEANUP_MAX_BATCHES (10) times per cycle, stopping early once a batch deletes fewer than ORPHAN_CLEANUP_BATCH_SIZE rows. Each DELETE remains bounded (≤100k, short locks, no long single statement), while per-cycle throughput rises to ~1M rows so backlogs drain ~10x faster. The per-cycle cap keeps monitor_db responsive. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
75bafabeee |
perf(monitor): hash active-root exclusion in retention delete (WIN-2088) (#9732)
* perf(monitor): hash active-root exclusion in retention delete The expired-job retention delete (delete_expired_jobs_batch) excluded jobs belonging to still-active root flows with `COALESCE(j.root_job, j.flow_innermost_root_job, jc.id) != ALL($3)`. That ScalarArrayOp is evaluated per candidate row as a linear scan of $3, so cost grows with the number of active root jobs. Express the exclusion as `NOT IN (SELECT u FROM unnest($3) u WHERE u IS NOT NULL)` instead. The subquery form lets Postgres build a one-time hashed SubPlan and apply it as a filter on the ordered index scan, giving O(1) membership per candidate while preserving the `ORDER BY completed_at ASC LIMIT` early termination. The `u IS NOT NULL` guard sidesteps NOT IN's null-trap semantics ($3 holds non-null PK ids). Measured on a 2M-row synthetic v2_job_completed (batch LIMIT 20000, 5-run min): active roots | != ALL (before) | NOT IN hashed (after) -------------|-----------------|---------------------- 100 | 108 ms | 104 ms 1000 | 168 ms | 105 ms 10000 | 719 ms | 131 ms Both forms return identical row sets (verified via EXCEPT, 0 diff). Neutral at small active-root counts, ~5.5x faster when many flows are active. Relates to WIN-2088 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(monitor): apply hashed active-root exclusion to log_cleanup mirror windmill-api-settings/log_cleanup.rs::delete_expired_jobs_batch carries a byte-identical copy of the retention delete and shared its prepared-query cache. Updating only monitor.rs removed that shared cache entry and broke the SQLX_OFFLINE build of the mirror. Apply the same NOT IN (hashed SubPlan) rewrite so both copies converge on one cached query and the mirror gets the same speedup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fa3596885b |
fix: allow SQL args in managed // materialize scripts (#9733)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6d94865109 |
fix: optimize cleanup_job_perms_orphaned and job_result_stream cleanup queries (#9727)
The job_perms/job_result_stream_v2 orphan cleanups in monitor_db used `NOT IN` anti-joins, `RETURNING job_id` + `fetch_all` (loading every deleted UUID into memory) and no batch limit. On high-throughput instances these tables can accumulate tens of millions of orphaned rows, so a single execution ran for ~298s; because monitor_db awaits each iteration, the cleanup ran effectively continuously, saturating DB I/O and starving audit partition creation. Rewrite both deletes as bounded `NOT EXISTS` anti-joins selecting `ctid` with a LIMIT 100000, executed via `.execute()` (using rows_affected instead of fetch_all). Each run is now fast and bounded, while the 30s monitor cadence is preserved so the tables keep draining promptly. Fixes WIN-2088 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8dea38383f |
fix: prevent silent audit-partition outage via monitor watchdog + alert (#9729)
The monitor loop runs ~25 periodic tasks under a single join!, so any one stuck on a non-DB await (statement_timeout only bounds DB statements) freezes the whole loop indefinitely — silently halting audit-partition creation. Once the missing partition's date is reached, audit inserts fail; because login writes its audit row in the same transaction, that poisons the login tx and locks every user out. - Wrap monitor_db in a 600s timeout (> statement_timeout) so a stuck task can no longer freeze the loop; report a critical error and continue on timeout. - After creating partitions, verify the lookahead window is actually covered and raise a critical alert naming any missing partitions, turning a silent latent outage into an early page. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2879cbb65a |
feat(apps): opt-in sandbox isolation for published & raw apps (alpha) (#9420)
* feat(apps): sandbox published & raw apps with a scoped embed token Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: point ee-repo-ref at embed-token EE commit Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(apps): allow top-navigation from the sandboxed app iframe Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(apps): share app localStorage across apps via the embedder Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(apps): publisher disable-sandbox option with per-version viewer consent Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(sqlx): cache for disable-sandbox queries Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump ee-repo-ref to disable-sandbox EE commit Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): always sandbox the served raw-app wrapper + viewer fixes The raw-app wrapper served by get_raw_app_data now always carries `CSP: sandbox`. The publisher "disable sandbox isolation" opt-out is applied entirely on the viewer side, which (after per-version consent) builds its own same-origin blob wrapper — so the backend-served document stays isolated regardless of how it is reached, never via a relaxed real-origin URL. Also: - CORS on the global /apps_u mount so the opaque viewer can load custom-path public apps cross-origin. - Reject runnable-bridge messages unconditionally until the iframe is bound. - Relay the viewer's in-app hash up to the embedder address bar so deep links stay shareable (hash only; embedder keeps its own pathname). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(apps): render public raw apps single-iframe (drop embed token) Public raw apps now render directly on the real origin with a single opaque bundle iframe and the page credential, instead of the opaque viewer + scoped-token indirection. The author bundle stays isolated in its own opaque iframe (CSP-sandboxed); low-code apps, whose code runs in the viewer frame, keep the opaque viewer + scoped token. embed_token now reports raw_app and skips minting a token for raw apps; the access check still gates visibility. Also set disable_sandbox: None in the remaining Policy constructors so the full feature build (all_sqlx_features, enterprise, license) compiles. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: bump ee-repo-ref to single-iframe raw-app EE commit Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(apps): grandfather existing apps as legacy-unsandboxed + authed-only consent Existing apps are stamped by migration as `legacy_unsandboxed` so they keep running same-origin on upgrade — no breakage and no consent prompt. New apps are sandboxed by default; re-deploying an app clears the flag. The publisher `disable_sandbox` consent prompt is now shown only to authenticated viewers — an anonymous viewer has no session to expose, so the prompt was meaningless friction. embed_token reports `legacy_unsandboxed` and `authed`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: bump ee-repo-ref to legacy-unsandboxed EE commit Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(apps): deploy-time migration prompt for legacy-unsandboxed apps On the first re-deploy of a grandfathered (legacy-unsandboxed) app, the publisher must explicitly choose: enable sandbox isolation (the flag is cleared → the app becomes sandboxed) or keep running without isolation (→ disable_sandbox, with per-version viewer consent). updatePolicy() no longer carries the legacy flag through a deploy, so the choice is what sticks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(apps): disable the sandbox-isolation toggle until the app is deployed The Deploy-drawer "Disable sandbox isolation" toggle called setPublishState() — which updates the app by path — even before the app was first deployed, when the path is empty, throwing an error. Guard it with disabled={!savedApp}, matching the adjacent visibility toggle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(apps): sandbox the in-workspace low-code app viewer in an opaque iframe Extend the opaque-origin iframe isolation to the logged-in /apps/get viewer. /apps/get becomes an embedder that keeps the workspace chrome + Edit button and renders the app inside a cookieless, chrome-less /app_embed viewer route, handed a scoped embed token minted from the member's session. The app frame runs in an opaque origin (no allow-same-origin), so it cannot reach the member's session cookie or window.parent. - apps.rs: get_app_embed_token_for_path (authed, by-path, scope + RLS gated); mint_app_embed_token grants a path-scoped apps:read:{path} so the viewer can load its own app definition and no other - lib.rs: CORS on /apps (bearer-token only, no cookies) for the opaque viewer's by-path reads - new /app_embed/[workspace]/[...path] viewer route (private analog of /public) - PublicAppFrame: viewerUrl prop to point the opaque iframe at the viewer route Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(apps): unify in-workspace app viewers on the shared sandboxed path Route every in-workspace app display (low-code and raw) through the same PublicAppFrame -> PublicApp machinery as the public viewer, so the sandbox / legacy-unsandboxed / disable-sandbox-consent behavior is identical on every page. - new InWorkspaceAppViewer renders both app types via PublicAppFrame; /apps/get and /apps_raw/get become thin wrappers over it - /apps_raw/get previously rendered RawAppPreview directly (always isolated, with no legacy-grandfathering or consent handling); now consistent with the rest - retire the legacy same-origin raw viewer /apps/get_raw/[version] and re-point the apps-list row to /apps_raw/get; remove the dead /apps_raw/[ws]/[version] route - load the raw bundle secret in the shared viewer (getAppByPath doesn't return it) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): address PR review feedback (scope + policy hardening, nits) - require handler-level apps:read on list_apps / list_search_apps so a scoped embed token cannot read app definitions through the list endpoints. The route layer treats apps:run as satisfying read; the handler check (which does not) closes the gap. - treat legacy_unsandboxed as backend-owned: strip any client-provided value in create/update so it can only be set by the grandfather migration, not the API. - document mint_app_embed_token's caller-verifies-access contract. - use Button's declared onClick prop for the consent action (was onclick, which fell into the rest-spread and bypassed the component's click handling). - test: lock that the embed scopes cannot satisfy domain-level apps:read. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(apps): document embed-token endpoints in openapi + fix doc nit Second-round review nits: - add the three app embed-token endpoints (apps/embed_token/p/{path}, apps_u/embed_token/{secret}, and the EE apps_u/embed_token_by_custom_path) plus the EmbedTokenResponse schema to openapi.yaml; note .html on get_data - mint_app_embed_token doc: "Both" -> "All" (it lists three call sites) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(apps): bound embed-token scopes to the caller's own The embed-token mint now enforces ensure_scopes_within_caller, so the minted scope set is always within the calling credential's own scopes (a no-op for regular unscoped sessions). Adds a unit test locking the boundary and documents the contract on mint_app_embed_token. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): raw-app ctx in external embeds + page credential in direct render - RawAppPreview: engage the storage relay only in opaque frames (probe Web Storage instead of just window.parent), so a public raw app embedded in an external iframe hydrates ctx/storage directly; add a relay-timeout fallback so an unresponsive parent can never stall the ctx handshake. - PublicAppFrame: in direct render, expose the page's own bearer credential through the AuthToken context (JWT public URLs), matching the previous route behavior; opaque-viewer mode keeps the embed token. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(apps): sandbox isolation UI polish + COI embed support for raw apps - Deploy drawer: move the sandbox toggle out of "Public URL" into its own "Sandbox isolation" section (the setting applies to every viewing surface, not just the public URL), with positive phrasing, visible helper text, and state-aware alerts (warning when disabled, info for pre-isolation apps). Toggling it now toasts its own message instead of the login-mode one. - Extract the deploy-time migration prompt into a shared LegacySandboxMigrationModal built on the common Modal component, and wire it into the raw app editor header too (it previously had no prompt, so re-deploying a pre-isolation raw app silently changed behavior). updateRawAppPolicy now also drops the backend-owned legacy flag, matching the low-code updatePolicy. - Viewer consent prompt: use the common ConfirmationModal and show the app path (new appPath prop) instead of the route pathname, falling back to "this app" when the path isn't known yet. - COI embeds: propagate the wm_coep opt-in to the raw-app wrapper document and have the backend assert COEP require-corp on it when the flag is present — required for the bundle iframe to load when the public app page is embedded inside a cross-origin-isolated page. Previously this only worked in dev because the Vite proxy injects the header; the production response lacked it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(apps): app navigation parity across sandboxed and direct viewers - Navbar component: same-app items relay query + hash to the embedder page (which mirrors them onto the root URL, keeping its own pathname and transport params), app items navigate the top page through a validated wm_embed_navigate relay instead of the cookieless viewer iframe, and external items keep opening a new tab. Selected-item detection now recognizes the /app_embed viewer route and ignores transport params. - Frontend-script `goto` and button `onSuccess: gotoUrl`: same-window navigation goes through a shared appNavigateSameWindow helper that relays to the embedder inside the opaque viewer (same-origin paths SPA-navigate, http(s) URLs do a full load, other schemes rejected) and keeps plain window.location everywhere else. - /apps/get and /apps_raw/get: key the viewer by workspace/path so in-route navigation fully remounts it — previously the URL changed but the app (and in sandbox mode its path-scoped token) did not follow. - wm_embed/wm_embedder_origin added to the reserved query params so they no longer leak into the app's ctx.query. - Raw apps: drop the sandbox attribute entirely for the unsandboxed (grandfathered/consented) blob path, matching the pre-isolation viewer exactly — the attribute added no isolation there and sandboxed popups (e.g. OAuth flows). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): preserve grandfathered policy across updates + in-workspace viewer parity Round of compatibility hardening so pre-existing apps behave exactly as before on every surface: - `legacy_unsandboxed` is now preserved across app updates unless the payload explicitly clears it (`false`, sent by the editor's migration prompt and the sandbox toggle). Unrelated update paths — CLI / git-sync redeploys, publish-mode toggles, cross-workspace promotion — no longer silently drop the grandfathering. Clients still can never SET the flag. - The embed-token endpoints (secret, path, EE custom-path) read only the sandbox-decision policy fields, leniently, and no longer mint a token for raw / legacy / disable_sandbox renders: the token is only consumed by the sandboxed low-code render, and minting for the others wrote a useless token row per view and could fail the render for scope-restricted callers. - In-workspace viewer parity with the pre-sandbox `/apps/get`: new `inWorkspace` mode on PublicApp (no "Powered by Windmill" badge / user overlay, no HTML-result approval gate, column flex wrapper, `hideRefreshBar` honored again), and the page's query/hash are forwarded into the opaque viewer so `ctx.query` / `ctx.hash` reach the app. - Raw apps: `window.ctx` is always `{ctx, workspace}` again (anonymous viewers of pre-existing bundles rely on `ctx.workspace`), and the runnable bridge's job-id scoping now applies only to sandboxed renders (`gateJobIds`) — an unsandboxed bundle holds the same credential as the bridge, so gating there only broke pre-existing apps polling persisted or runnable-returned job ids. - Document `disable_sandbox` / `legacy_unsandboxed` in the openapi Policy schema; add a unit test for the lenient policy read. - bump ee-repo-ref to the matching EE commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): keep share-link viewer credentials out of the isolated app context The JWT path segment of authenticated share URLs is an embedder-side credential, consumed only to mint the scoped embed token. Two transport channels still copied it into the isolated frame where app-authored code runs: - the opaque viewer iframe src defaulted to window.location.href — the public and custom-path routes now pass a sanitized viewerUrl (JWT segment stripped, query/hash preserved, captured once so the hash relay does not reload the iframe); - document.referrer on the same-origin iframe navigation carried the full embedder URL — both app iframes now set referrerpolicy="no-referrer" (sandboxed renders only for the raw bundle iframe, keeping exact legacy parity; nothing reads the referrer). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(frontend): drop unused import inherited from main merge `slide` import in AssistantMessage.svelte (from #9539) turns `npm run check` red on this branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): redirect the removed raw-app viewer path to the unified viewer The old same-origin raw-app viewer route (/apps/get_raw/{version}/{path}) was removed in favor of the sandboxed unified viewer. Re-add a thin client route at the old path that redirects stale bookmarks to /apps_raw/get/{path}, preserving query + hash (the pinned version is dropped — the unified viewer shows latest). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): narrow embed-token scopes and base consent on browser session - Embed token: resource access is metadata-only (list/type/exists) via a `resources:run` marker — resource values (get/get_value/get_value_interpolated/ list_search) are no longer reachable. Job reads are by-id only: an `app_embed` sentinel blocks the workspace-wide job enumeration/export routes (jobs/list, list_filtered_uuids, queue/list, completed/list, queue/export) while by-id result polling keeps working. - disable_sandbox consent now gates on whether the browser holds any Windmill session (cookie-only whoami) rather than workspace-scoped auth, so a viewer logged into a different workspace is still prompted before a same-origin render. - db-explorer: resolve the MySQL database name server-side (the metadata query already falls back to DATABASE()) instead of reading the resource value client-side; getTablesByResource derives the default db from the schema. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(apps): trim embed-scope and consent comments Reduce duplication — state the resource/job route exclusions and the workspace-session-vs-cookie rationale once at their source and reference them elsewhere; drop contrast/justification phrasing. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(apps): make app sandbox isolation opt-in (alpha) Replace the disable_sandbox + legacy_unsandboxed policy pair and the per-version viewer consent with a single positive `sandbox` opt-in flag. Apps are unsandboxed by default (same-origin, full session — the pre-isolation behavior), so existing apps are unchanged and no migration is needed. Publishers opt an app into isolation from the deploy drawer, flagged alpha. - Policy.sandbox: Option<bool>; EmbedTokenResponse -> {token, expiration, raw_app, sandbox}; mint an embed token only for sandboxed low-code apps. - Drop the legacy-unsandboxed migration and the deploy-time migration prompt; remove the consent modal and the browser-session probe. - Deploy drawer: a single "Sandbox isolation" toggle (alpha), off by default, shared by the low-code and raw editors. - Bump ee-repo-ref to the companion EE commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): confine embed token to its intended user/folder/job routes The embed token's broad read scopes spanned whole domains while the matching routers are CORS-enabled for the opaque app iframe: - users:read / folders:read were domain-wide, so the token could reach users/list, users/list_usage, users/username_to_email/*, folders/list, etc. Restrict to an app_embed-sentinel allowlist: only users/whoami and folders/listnames; deny the rest of those domains. - jobs:read allowed jobs/completed/export, missed by the job denylist. Add it alongside jobs/queue/export. Extend the embed-scope allow/deny test matrix to cover all of these. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(apps): align sandbox comments with the opt-in model The consent prompt, deploy-time migration, and legacy-unsandboxed grandfathering were removed when sandbox isolation became an opt-in policy flag; update the comments that still described them so they match the two-state (default-unsandboxed / opt-in-sandboxed) reality. Comments only, no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): confine embed-token job reads to runs the app launched App component jobs are stamped `created_by = the viewer`, so an embed token reads its own runs via the launched-by-viewer fast path. The token then also inherited the viewer's broader job access (share links, folder ACLs, admin RLS), letting user-authored app JS reuse it to read unrelated jobs by id. Stop embed tokens at the fast path: only jobs the viewer launched, never those merely visible to them. Return NotFound so the untrusted app can't probe existence. Regression test: an embed token reads its own launched job but is denied the foreign job (result/logs/getupdate) an admin viewer's normal token can read. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): allowlist embed-token apps/jobs routes + scope run to the app The embed token's apps:run/jobs:read reached more than a running app needs. Replace the job denylist with strict per-domain allowlists on the app_embed sentinel: - Apps: only the app's own definition (apps/get/p/<path>) and the public app-serving endpoints (apps_u/*). Denies workspace app inventory (exists, custom_path_exists, list, list_paths*). - Jobs: only the by-id poll routes the frontend JobLoader uses. Denies job counts and the job_signature/resume_urls capability-minting routes (the by-id reads remain confined to the app's own runs). Drop unqualified apps:run from APP_EMBED_SCOPES; mint apps:run:<path> instead and authorize apps:run:<requested path> first in execute_component, so the token can only run its own app's components, not another app's. Extend the embed-scope route matrix and add a path-scoped run unit test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(apps): clarify the sandbox toggle vs the on-behalf-of model The deploy-drawer sandbox copy leaned on "session" in a way that collided with the on-behalf-of permissioning right above it. Reword it to say the toggle governs what the app's browser-side code can reach in the viewer's browser — distinct from who its runnables execute as — and rename the label to "Isolate the app from the viewer's browser session". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): path-scope embed-token S3 download to its own app The apps_u/* allowlist also admitted apps_u/download_s3_file/<path>, whose handler authorized any authenticated caller — so an embed token minted for app A could download app B's S3 files via B's on-behalf policy. Add the same path-scoped guard execute_component uses: download_s3_file_from_app now checks apps:read:<path> first, confining the token to its own app. Other path-taking apps_u routes are already covered (writes lack apps:write; embed_token/p path-checks; public_resource is type-constrained). Extend the path-scoping unit test to cover apps:read (download) alongside apps:run (execute). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): path-scope public-app-by-secret read to the embed token's app The apps_u/* allowlist admitted apps_u/public_app/<secret>, whose handler only checked the viewer's read access — so an embed token minted for app A could read app B's definition by secret (confused deputy via the viewer's identity). get_public_app_by_secret now binds a scoped caller to the resolved app with check_scopes(apps:read:<path>), confining it to its own app; unscoped sessions and anonymous access are unchanged. get_raw_app_data needs no binding (pure secret capability, no caller identity). Document the full set of app-resolving handlers the path-scoped read covers. Bump ee-repo-ref for the companion custom-path fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): preserve pre-sandbox behavior for db-explorer, edit link, jwt Three behavior-parity fixes for non-sandboxed (existing) apps that the sandbox-isolation refactor changed incidentally: - DB-explorer MySQL table picker: when the connection can see multiple non-system schemas, label the default db's tables unprefixed again. The resource-value read was removed globally, so identify the default db from the introspection script's `DATABASE() AS default_db_name` (carried on SQLSchema.defaultDb) instead of guessing "the single schema key". Equivalent to the prior resource.database match; editor-only (table picker). - In-workspace Edit button: restore `?nodraft=true` on both /apps/get and /apps_raw/get, so opening the editor from the viewer loads the deployed version, not a draft. - Custom-path (/a) viewer: restore the "could not authenticate user with jwt token" toast when a path JWT fails to resolve a user, instead of silently falling through. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): confine embed-token S3 downloads to the app's own keys/outputs download_s3_file_from_app authorized any authenticated caller for any S3 key (opt_authed.is_some() bypass). A sandboxed app's embed token carries the viewer's identity, so app-authored JS could fetch arbitrary S3 keys readable by the on-behalf identity, beyond the app's own declared keys or outputs. Route app embed tokens through the same allowlist as anonymous viewers — the app's declared allowed_s3_keys, or files produced by this app's own component runs — instead of the authed bypass. The produced-files check is parameterized by created_by (the embed viewer for a token, else anonymous) so a sandboxed app's own S3 outputs still render while arbitrary keys are denied. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): let embed tokens cancel their own jobs; gate cancel to launcher A sandboxed low-code app supersedes an in-flight component run on re-run by canceling it, but the embed token only had jobs:read, so cancellation silently failed and prior jobs ran to completion. - Permit the by-id jobs_u/queue/cancel POST for app_embed tokens at the route layer (the only write reachable through the existing by-id allowlist). - Gate cancel_job_api: an app_embed token may cancel ONLY jobs it launched (created_by == viewer). cancel_job_api had no other per-job ownership check, so this also confines the token instead of letting it cancel any job by id. - /app_embed now sets workspaceStore so cancellation targets the right workspace instead of an empty/stale one in the cookieless iframe. Add a shared has_app_embed_sentinel helper; cover cancel in the route matrix and the jobs_read_auth integration test (own job cancelable, foreign denied). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): drop get_root_job_id from the embed-token job allowlist Audit of the embed token's reachable job routes: get_root_job (jobs_u/ get_root_job_id) has no access check in its handler at all — it returns any job's root-job id by id — and the app runtime never calls it. Remove it from the by-id allowlist so the embed token can't probe a foreign job's flow lineage; add a denied-route assertion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(apps): scope sandboxed-app localStorage per app Sandboxed apps shared one localStorage store (one key on the real origin), so an app could read or clobber another app's keys — and, with job ids stashed there, reuse its embed token to read another app's job. Scope the backing store per app. The embed-token endpoints now return the resolved app_path (EmbedTokenResponse; not a new disclosure — the viewer already receives the path when it loads the app). PublicAppFrame (low-code) and RawAppPreview (raw) key their backing store by it: wm_apps_localstorage:<app_path>. Same app shares one store across its public and in-workspace surfaces; different apps are isolated. Unsandboxed apps are unaffected (real same-origin localStorage, as before). Bump ee-repo-ref for the companion custom-path change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): scope embed access checks to embed tokens + key app storage by workspace - Apply the path-scoped read/run checks on the public-by-secret read and the component run path only when the caller is an app embed token, so other caller types keep their prior access. - Key the sandboxed app's backing client storage by workspace + path instead of path alone, and return the resolved workspace from the embed-token endpoints so the custom-path viewer can derive it. - Show a clear message instead of an indefinite loader when the viewer route is opened outside its embedder. Bumps ee-repo-ref to 5b8476b. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): mint embed tokens only from the trusted embedder caller An app embed token must not reach the embed-token mint endpoints; refresh minting stays with the embedder session/JWT. Enforced at the scope route layer and at the mint chokepoint, with a route-matrix regression test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(apps): support S3 upload and frontend-script S3 download in sandboxed apps Sandboxed apps run with a scoped embed token (no cookie). Let the app's S3 file-input upload and the frontend-script download({s3}) helper work in that context: upload is reachable with apps:run and re-checked per-app at the handler; the script download routes through the app-scoped apps_u endpoint with the embed token instead of the cookie-authed job_helpers path. Default (unsandboxed) apps are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: update ee-repo-ref to b0cb761bf9852974e571b2978032d310cc998517 This commit updates the EE repository reference after PR #600 was merged in windmill-ee-private. Previous ee-repo-ref: e673c714a4618fdb72353a475f49c748e6016642 New ee-repo-ref: b0cb761bf9852974e571b2978032d310cc998517 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> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
e82a6a6830 |
chore(main): release 1.736.0 (#9720)
* chore(main): release 1.736.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.736.0 |
||
|
|
e16061df06 |
fix(health): detect read-only replica via pg_is_in_recovery() (#9722)
The /api/health/status database check used `SELECT 1`, which succeeds even on a read-only standby. After a PostgreSQL failover where the primary becomes a secondary, the health check kept reporting healthy while all writes failed with "cannot execute INSERT in a read-only transaction", so Kubernetes liveness probes never restarted the pod. Use `SELECT NOT pg_is_in_recovery()` instead: it returns true on a primary and false on a standby, so a read-only replica is now reported unhealthy. Result handling checks the returned bool (Ok(Some(true))) rather than just query success. Fixes WIN-2085 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3bf5b72afa | fix(drafts): stop mis-filing workspace-blind legacy drafts on migration (#9725) | ||
|
|
6f4017d694 |
feat(ai-chat): workspace AI chat skills (SKILL.md upload + read_skill tool) (#9648)
* feat(ai-chat): workspace ai_skill table + CRUD API Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): AI Skills workspace settings tab with SKILL.md upload Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): advertise skills in global system prompt + read_skill tool Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ai-chat): move custom skills into AI settings (paste or folder) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): cap folder import (depth<=3, max 50 skills, confirm dialog) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style(ai-chat): give import folder its own labeled subsection Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): resolve svelte-check never-narrowing in skills preview Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address ai skills review issues * fix: validate ai skills and reload workspace list * fix(ai-chat): spec-align skill validation and cap skills per workspace Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): reject duplicate skill uploads, audit skill names Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): sync deref openapi specs with skill validation rules Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d5cb944cf9 |
fix(frontend): ensure type:object in test_run_flow tool schema for Anthropic (#9721)
Flows with no defined inputs can produce a sparse schema (e.g. { order: [] })
that lacks the "type": "object" field. buildSchemaForTool spread this schema
into the tool parameters as-is, so the Anthropic API rejected the tool
definition with `400 invalid_request_error:
tools.N.custom.input_schema.type: Field required`. The existing fallback in
anthropic.ts only triggers when parameters is falsy, but the sparse schema is
truthy.
Default type:object before spreading the schema in buildSchemaForTool, and
backfill type/properties/required in FlowAIChat's getFlowInputsSchema as a
defense-in-depth measure.
Fixes WIN-2087
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
e19594df2a |
fix: re-enforce scoped API token boundaries across handlers (#9712)
* fix: re-enforce per-path token scope on store rename, delete and interpolation Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: enforce token scope on workspace export and resume-url minting Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: enforce per-item and runnable scope on trigger create paths Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: enforce app write scope before persistence and on rename Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: enforce scope containment on mcp oauth approval Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: scope mcp endpoint-proxy jwt to the proxied route Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: treat resource-linked variables and resources as covered by resource scope Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: only require variables:read for plaintext-secret workspace export Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: handle singlestepflow resume, reject empty mcp grant, scope var-skipped tarball Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6e96f90065 |
fix(frontend): destroy old WebsocketProvider on workspace switch in MultiplayerMenu (#9719)
Switching workspaces created a new WebsocketProvider without destroying the old one. The leaked provider kept reconnecting, causing alternating websocket traffic between old and new workspace rooms and flickering in the Live Activity sidebar. Add a disconnectWorkspace() cleanup that destroys the provider and resets connected/awareness state, call it at the start of connectWorkspace() before creating a new provider (matching ScriptEditor.svelte), and run it from an onDestroy hook so the provider is torn down on unmount. Fixes WIN-2086 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
83ec0dd07a |
chore(main): release 1.735.0 (#9700)
* chore(main): release 1.735.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.735.0 |
||
|
|
e20a27745a |
fix(frontend): strip raw-app post-deploy diff noise (raw_app/lock/data) (#9706)
* fix(frontend): strip raw-app post-deploy diff noise (raw_app/lock/data)
The raw-app editor's Diff drawer showed a spurious deployed-vs-current
diff immediately after deploy, even with no edits: `raw_app: true`, a
server-recomputed inline-script `lock`, and an empty `data` mismatch.
These come from comparing the deployed app row (from getAppByPath) against
the editor's current value, which differ on server-managed fields the
editor never carries, on inline-script locks (recomputed at deploy, cleared
on edit), and on `data` (the deployed row omits an empty `data` while the
editor always carries the default `{tables: []}`).
Add `stripRawAppDiffNoise` (strip server columns, null inline locks,
canonicalize data) and apply it symmetrically to both diff sides in the
editor header. For the session/compare draft diff, the draft is stored flat
(files/runnables/data top-level) while the deployed row nests under `value`,
so add `canonicalRawAppDiffValue` (= appSourceToDraftValue + stripRawAppDiffNoise)
and route both sides through it in getDraftDiffValues. Both diff surfaces now
share the same normalizer.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(frontend): use canonicalized current value in deploy-drawer raw-app diff
The Deploy drawer's "Diff" action still built the current side inline from
raw editor state, bypassing stripRawAppDiffNoise — so inline-script `lock`
and data-shape noise could resurface via Deploy → Diff even though the
top-level Diff button was already fixed. Route it through `currentDiffValue`
(and strip the savedApp fallback) so both entry points behave identically.
Addresses Codex review finding on PR #9706.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
e403f92d7e |
fix: enforce job_dir containment when writing module files (#9703)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8a0b0abead |
fix: ignore NotFound errors when deleting log files from object store (#9707)
* fix: ignore NotFound errors when deleting log files from object store Periodic and manual log cleanup delete log files from instance object storage. S3's DeleteObjects silently ignores missing keys, but GCS returns a 404 for each individual delete, which the object_store crate's default delete_stream surfaces as Error::NotFound. This produced noisy error/warning logs on every cleanup cycle even though the cleanup succeeded (DB records are removed regardless). Treat a NotFound delete as a successful no-op in both delete handlers: - monitor.rs: skip logging NotFound errors - log_cleanup.rs: count NotFound as deleted instead of an error Fixes WIN-2081 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: report 404 (already-absent) count in object store log cleanup Track delete calls that returned 404 (object already absent) separately from real deletes so operators can see how many of the attempted deletes were no-ops, instead of those numbers silently folding into s3_deleted. - monitor.rs: emit a final info summary per cleanup cycle: "N deleted, M already absent (404), K failed" (only when work occurred) - log_cleanup.rs: add s3_not_found to LogCleanupProgress (serde default for backward-compatible deserialization of in-flight rows), thread it through s3_bulk_delete and all call sites, and log a final summary on release - openapi.yaml + generated client + ObjectStoreConfigSettings.svelte: surface the 404 count in the manual cleanup status UI Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: import ObjectStoreError directly from object_store_reexports The object_store_reexports module already re-exports object_store::Error under the name ObjectStoreError, so `Error as ObjectStoreError` failed to resolve (no `Error` in that module). This compiles only behind the parquet feature, which the local dev `cargo watch` doesn't enable, so it was caught by CI's full-feature check rather than locally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ed016a5edb |
feat: clarify session draft bar tracks all workspace draft changes (#9714)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ef4962e52a |
fix(oauth): restore bring-your-own CC token URL override (#9711)
* fix(oauth): restore bring-your-own CC token URL override Re-add the optional resource-level token URL field for client-credentials connections, sent only with the caller's own client_id/secret. Updates the connect/create_account request schemas and bumps the EE ref. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(oauth): keep openapi-deref unchanged from main The dereferenced specs are not regenerated per-PR (already stale on main, CI only lint-validates them). Revert the incidental full regen so the PR diff stays focused on openapi.yaml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(oauth): host-pin CC token URL override server-side Add is_instance_templated_cc so the EE handlers can reject a bring-your-own token URL override for {instance}-templated providers (defense in depth for direct API callers). Bump the EE ref. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(oauth): serve cc_token_url in deref specs, enforce CC grant gate Add cc_token_url to the dereferenced OpenAPI artifacts served at /openapi.yaml and /openapi.json so generated clients see the new field (kept to a focused add rather than a full regen). Bump the EE ref for the grant-gate enforcement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to de49fda2320504ad9e7d2d31c7033d71dbf6ca43 This commit updates the EE repository reference after PR #625 was merged in windmill-ee-private. Previous ee-repo-ref: a939228d0314c21937687d43c8ef354bdc87c40e New ee-repo-ref: de49fda2320504ad9e7d2d31c7033d71dbf6ca43 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> |
||
|
|
b0ddcf31e4 |
ci: add path-gated AI agent + ai_evals smoke workflows (#9640)
* ci: add path-gated AI agent integration tests workflow Runs integration_tests/ai_agent_tests against real LLM providers (Anthropic/OpenAI/Google) only when AI-agent backend code or the tests change, since runs make paid LLM calls. Adds a conftest fixture that skips provider-parametrized cases whose API keys are absent, so CI exercises only the providers it has secrets for. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: add path-gated ai_evals global-mode smoke workflow Runs the global AI chat eval (global-test1) across one cheap model per provider (Anthropic/OpenAI/Google/DeepSeek) only when the eval harness or copilot chat code change, since runs make paid LLM calls. Builds Windmill CE from source as the AI proxy; global tools/drafts run in the Vitest bridge. Gates on the deterministic draft pipeline (run succeeded + produced a draft + used write_script), not the variable LLM judge score. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: run AI smokes on PR ready-for-review instead of every push Switch the pull_request trigger from `synchronize` (every commit) to `ready_for_review`, with a job guard skipping draft PRs, so the paid LLM runs only fire when a PR is marked ready to merge (plus push-to-main and manual dispatch). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai_evals): lazily load cli mode so non-cli evals skip the cli toolchain The entrypoint eagerly imported modes/cli, which pulls the wmill CLI guidance modules and their JSR deps (@cliffy/*). Global/flow/script/app runs then crashed with "Cannot find module '@cliffy/ansi/colors'" when the cli workspace deps were not installed. Import createCliModeRunner dynamically inside runCliBenchmark instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai_agent): raise low max_completion_tokens to OpenAI's 16 minimum OpenAI's /v1/responses rejects max_output_tokens < 16 with a 400, failing test_low_max_tokens for openai. 16 still exercises a truncated response. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: run ai_evals workflow on Node 22 for the frontend undici 8.x dep The Vitest bridge loads frontend/node_modules/undici@8.x, which requires Node >=22.19; Node 20 failed with "webidl.util.markAsUncloneable is not a function" when loading vitest.config.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai_evals): run frontend evals autonomously + give global-test1 more turns Frontend evals (flow/script/app/global) ran the production chat prompt, which assumes an interactive human — so cheaper models burned their turn budget asking for confirmation, waiting for approval, or presenting a plan, sometimes hitting maxTurns without producing a draft. Append a shared autonomy note in baseEvalRunner (the path all frontend modes share, mirroring cli mode): act directly on clear requests; only ask on genuinely ambiguous ones (preserving the askUserQuestion cases). Also raise global-test1's maxTurns 8 -> 10 so a model that over-explores still converges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(ai_evals): watch draft/prompt deps outside copilot/ The global eval runs production frontend code in-process, so the smoke's behavior depends on files outside frontend/src/lib/components/copilot/**: the draft model (userDraft.svelte.ts, userDraftDbSyncer.svelte.ts), script inference (infer.ts), and the chat system prompts ($system_prompts -> system_prompts/auto-generated). Add them to both push and PR path filters so a change there actually triggers the smoke that gates on draft production. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: skip direct provider tests without credentials * feat: add ai evals skip judge flag * fix: simplify ai evals ci gate * fix: simplify ai evals smoke gate * fix: handle ai eval workflow triggers --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
74a2329d2e |
feat(copilot): improve global-mode path selection + add path-selection evals (#9698)
* test: add global-mode path-selection eval cases with seeded user Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(copilot): guide global-mode path selection with injected folder list Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(copilot): tailor global-mode folder guidance for workspace admins Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(copilot): type folders_read; isolate global-eval user from store Addresses PR review: - Add folders_read to the User/whoami openapi schema and UserExt; the global prompt builder and eval harness now read it typed instead of via inline casts (regen the client to pick it up). - prepareGlobalSystemMessage takes an explicit user; the eval harness passes it rather than mutating the process-global userStore, removing the concurrency race (path cases no longer need --verbose). - Rewrite the path-selection case comment as a current invariant. - Add buildFolderGuidance unit tests in core.test.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ace7b68a28 |
fix: sanitize git credentials from ansible executor errors and logs (#9697)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6eb03d2590 |
round worker usage occupancy up to 5-minute chunks (#9702)
* feat: round worker usage occupancy up to 5-minute chunks Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 8b12fa14ef7969e948169eadf1bf672d7928e5b1 This commit updates the EE repository reference after PR #624 was merged in windmill-ee-private. Previous ee-repo-ref: 168498974150ff309658b2da43bce8444f13a765 New ee-repo-ref: 8b12fa14ef7969e948169eadf1bf672d7928e5b1 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> |
||
|
|
23bf6bf3da |
fix(frontend): deploy full script/flow draft from AI chat via shared module (#9642)
* fix(frontend): deploy full script/flow draft from AI chat via shared module Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): drop non-persisted priority/timeout from flow draft deploy The flow branch of the shared deployDraft set `priority`/`timeout` on the create/update body, but the backend does not persist those fields on flows (a direct API write returns them as null). Remove the dead fields and the unit-test assertions for them; the flow deploy still carries every config field the backend actually stores (tag, dedicated_worker, …). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): chat deploy resolves draft storage path (honor chosen path) The chat addresses drafts by their display/chosen path, but a draft_only item created in the editor lives at a synthetic `u/{user}/draft_{uuid}` storage key (chosen path held in the draft value). The shared deployer reads the draft via getScriptByPath/getFlowByPath at the path passed, so passing the chosen path 404'd. Resolve to the storage path via getGlobalDraftStoragePath before delegating; the deployer then deploys at the draft's own `path`. Regression from the deploy-unification: the old builder read the already-resolved draft and deployed at the chosen path directly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): chat raw-app deploy honors the draft's chosen path The raw-app branch deployed at the path the chat was addressed by (args.path), which for an editor-created draft_only raw app is the synthetic `u/{user}/draft_{uuid}` storage key, not the chosen path. Resolve the storage path and read the chosen path from the backend raw_app draft's `draft_path` (confirmed shape: getAppByPath{getDraft,rawApp}.draft.draft_path), then create/ update there — mirroring the script/flow storage-path resolution. Content still comes from the flat AppDraftValue, which the editor and chat both use. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): flush live draft before chat deploy; narrow raw-app catch Addresses review feedback on the AI-chat deploy: - Codex P1: script/flow deploy delegates to the shared deployer, which re-reads the persisted DB draft. An open editor's edit may still be parked in a debounced/disabled autosave, so the deploy could publish a stale draft and the post-deploy draft delete could drop the unsaved edit. Flush the draft's UserDraftDbSyncer key before delegating (always saves, like Ctrl/Cmd+S, since the user explicitly asked to deploy). - Cubic P2: the raw-app draft_path lookup caught all errors and fell back to the storage path, masking real failures (network/5xx). Only fall back on 404; re-throw other errors so the deploy aborts instead of deploying to the wrong path. Adds tests for both; updates the existing raw-app deploy tests to mock getAppByPath. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): flush raw-app draft before reading draft_path on chat deploy Codex P1 follow-up: the raw-app branch derives the deploy targetPath by re-reading draft_path from the persisted backend draft, but — unlike script/flow — didn't flush first. An editor rename mirrored into draft_path can still be parked in a debounced/disabled autosave, so an immediate chat deploy could read a stale draft_path and deploy to the old path. Flush the raw_app draft key before the getAppByPath read, mirroring the script/flow fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): abort chat deploy when pre-deploy draft flush conflicts/fails Codex P1 follow-up: the pre-delegation UserDraftDbSyncer.flush() resolves even when the save recorded a conflict (server has a newer version) or failed (network/5xx) — it does not throw. The deploy would then re-read a stale or conflicting persisted draft and publish it. Add flushDraftOrThrow(): after flush, check getConflict() and getState().state === 'failed' and abort with a clear message. Used by both the script/flow and raw-app deploy paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4a8a724895 |
feat: scope default instance db name to workspace (dt_/dl_) (#9699)
* feat: default instance db name to dt_/dl_ workspace scope Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: cap instance db name at 63 chars and add unit tests Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3a55800224 |
add whatsapp business icon (#9541)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
84cc043406 |
feat: link files & folders to the global AI chat (#9520)
* feat: add file attachments to the global AI chat Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: add folder linking and file-type icons to chat attachments Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: persist linked files, add @-menu file tree, and polish chat file UI Persistence (survive reload, scoped to session.id): - IndexedDB store (attachedFilesDB) holding Blob snapshots (every browser) and re-grantable File System Access directory handles (capable browsers) - restore on session activation; re-grant locked handles on the next send; flush in-memory items when the session persists; GC on session delete - capability via feature-detection (fsAccess), never UA sniffing - folders auto-refresh (live re-enumerate + reconcile) on each send @-mention file picker: - Files branch in ChatContextPicker (new DrillPicker architecture); a linked folder's files render as a nested directory tree, picking inserts @filename - attached-file mentions highlight in the input just like context mentions UI polish: - file/folder chips reuse the context-element chip style (icon -> X on hover) - file + context badges sit above the fork/draft bar - disabled dropdown items can surface an explanatory tooltip (DropdownV2Inner) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor: deepen the attached-files store — folders as first-class objects Two seam fixes from an architecture pass, no behaviour change: - addFolder(dirHandle) now enumerates internally (same junk-filtered walk used on restore/refresh), so callers never pre-enumerate. The dead drop-walkers (collectDroppedEntries, filterFolderPickerFiles) are deleted; isIgnoredPath/MAX_FOLDER_FILES move next to enumerateDir in fsAccess. - The store exposes `folders` (name + aggregate status + children) and `standalone` as derived views, so the bar, the @-menu picker, the folder chip and the system-prompt roster stop re-grouping the flat row list and re-deriving folder status. Placeholder rows (isFolderRoot) become an implementation detail; the roster renders a locked folder as one line. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: drop the redundant context-badge row in the global chat In GLOBAL mode selected context already appears as a highlighted @mention in the input (deleting the mention deselects), so the hoisted badge row above the chat duplicated it. File chips keep their row — attachments aren't represented in the input. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: harden attachment edge cases found in review - requestReadPermission/queryReadPermission never reject (the spec rejects with SecurityError when user activation is missing — now mapped to denied/prompt), and sendRequest wraps attachment upkeep in try/catch, so a permission hiccup can never silently swallow a Send. - regrantLocked expands before dropping the locked placeholder: when the re-granted directory is gone from disk, the folder now shows "unavailable" instead of vanishing into a zombie that resurrects locked on the next reload. - addFolder: re-picking a locked/unavailable folder relinks it (natural recovery gesture); a genuine second folder with the same basename gets a visible "already linked" rejection instead of a silent no-op. - fileEngine: readFile clamps its byte slice to maxChars*4 before decoding and streamLines caps its per-line buffer, so newline-sparse files (minified JS, single-line JSONL) can't materialize unbounded strings; corrected the scan-cap comment's claim about catastrophic backtracking. 4 new unit tests (41 total). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: surface folder-picker failures instead of swallowing them `pickDirectory` caught every `showDirectoryPicker` rejection and returned undefined, so a real failure (an enterprise/browser policy blocking the File System Access API, a lost user-activation, …) was indistinguishable from a no-op — the picker just silently never opened. Now only `AbortError` (user dismissed the dialog, or CDP intercepted it under automation) is treated as a cancel; anything else is rethrown and `linkFolder` surfaces it as a toast. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: support folders in browsers without the File System Access API Folders can now be added in every browser, not just Chromium. Where the File System Access API is absent (Firefox/Safari), a dropped or picked folder's files are snapshotted into the browser (via a webkitGetAsEntry drop-walk or a `webkitdirectory` input) instead of linked as a live handle, and grouped/displayed identically to a File System Access folder. The dropdown item reads "Link folder" when a live link is possible and "Add folder" otherwise, with a tooltip pointing to Chrome/Edge for a live link. Snapshot folder children persist their `folder`/`relPath`, so they regroup into the same folder chip on reload. Removes the arbitrary file-count caps (500 per folder, 100 total) — only the browser's memory / IndexedDB quota now bound a folder. Junk paths (node_modules/.git/dist/dotfiles) are still skipped, folder-contents only, so an explicitly attached standalone dotfile is kept. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address review feedback — index-race guard + read_file line numbers Both automated reviewers flagged two issues on the attached-files feature: - (P1) Stale async indexing could corrupt a newer file. `#indexFile` applied its unawaited `buildLineIndex` result by display name, so if a row's file was swapped while indexing was in flight (remove + re-add a same-named file, or a folder refresh re-indexing an edited file) the stale result stamped the wrong lineIndex/lineCount — and `read_file` then sliced the new Blob with old offsets. Now patched via `#patchFile`, which applies the result only while the row still holds the exact file object that was indexed. - (P2) `read_file` promised "line-numbered context" but returned raw text. It now prefixes each line with its absolute 1-based number (`<n>→<content>`), matching the tool contract; `numberLines` lives in fileEngine and is unit-tested. Adds regression tests: a deterministic stale-index race test (controlled buildLineIndex ordering) and numberLines coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address re-review nits — read_file pagination + searchFiles regex state - read_file: when the maxChars cap truncated a window short of its requested end line, the pagination note still reported the full range and gave no/wrong resume point, so the model couldn't reach the unread lines. The note now reports the last line actually returned and resumes at the next unread line (advancing past a single over-long line rather than re-truncating it forever). - searchFiles: reset `regex.lastIndex` before each `.test()` — a caller-supplied `g`/`y` flag makes test() stateful and would silently drop matches. Not reachable from the current caller, but searchFiles is exported. Adds regression tests for both. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep an emptied live folder linked and refreshing A live (File System Access) folder carried its directory handle only on its child file rows. When the folder was emptied on disk, refreshFolders/#reconcileFolder removed the last child — dropping the only handle-bearing row — so the folder vanished from the chip bar AND was never re-enumerated again (files added back on disk weren't picked up until a reload). #expandFolder had the same gap on restore. Now #ensureFolderRow leaves one handle-carrying placeholder row when a folder has no readable children (keeps the chip visible and the live source alive), and drops it once children return; refreshFolders collects sources from placeholder rows too, and readyFiles never exposes a placeholder to the read/search tools. Adds a regression test (empty → still visible → file returns → picked up). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: trim read_file char-cap output to match its pagination note When the char cap cut partway into the line after some whole lines, readFile set the note/endLine to the last complete line but still returned the partial next line in `text` — so read_file showed (line-numbered) a line the note said would come on the next read. Trim the returned text back to the last complete newline so the body and the note agree. Test now asserts res.text for that case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: isolate search_files in a Worker (ReDoS) + path-aware folder dedup - search_files runs a model-supplied regex, and a catastrophic-backtracking pattern (e.g. /^(a+)+$/) can't be interrupted mid-test, freezing the tab. Run the search in a Web Worker (searchFilesInWorker) and terminate it on a timeout, returning "pattern too expensive" instead of hanging. Degrades gracefully to a main-thread search where Workers are unavailable / fail to load. - #isDuplicate keyed its content check on the file basename, so two distinct files sharing a basename under different folder subdirs (proj/a/index.ts vs proj/b/index.ts) were wrongly deduped and silently dropped from snapshotted folders. Key it on the relative path instead. Adds tests: worker result/timeout-and-terminate, and same-basename-different-subdir. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep an initially-empty live folder linked (placeholder + persist) addFolder only created rows / persisted the dir-handle when at least one text file was found, so linking a folder that's empty (or all-binary) at pick time was a silent no-op: no chip, nothing persisted, and refreshFolders had no source to re-enumerate when files were added later. Now it always leaves a placeholder (#ensureFolderRow) and persists the handle — matching the became-empty behavior — so the folder stays visible, survives reload, and picks up files added afterward. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep empty-folder placeholders out of the real-file name space The placeholder row for an empty live folder uses name = folder, which could collide with a standalone file of the same name: addFiles deduped the file against the placeholder, removeFile(name) dropped both rows, and #uniqueName pushed the file to a "(2)" suffix. Placeholders are managed via removeFolder and never read by the tools, so exclude isFolderRoot rows from #isDuplicate, removeFile, get(), and #uniqueName. Adds a placeholder/standalone collision test. (codex's other nit — @-mentions not highlighting filenames with spaces — left as a known cosmetic limitation per the chosen scope.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: highlight @-mentions of filenames containing spaces A file mention was inserted verbatim as `@my file.txt`, but the highlighter regex `@[\w/.\-\[\]]+` stops at the space, so only `@my` was parsed/highlighted and the mention didn't behave as advertised. Introduce a small shared `mention` module: names with whitespace are inserted in a bracketed form `@[my file.txt]`, and the shared regex + `mentionTitle` parse both bare and bracketed tokens. Both insertion entry points (the inline `@` picker in ContextTextarea and the toolbar path in AIChatInput) now use `formatMention`, so the full name highlights. Verified in a real browser: `@[my file.txt]` renders as a single highlight span. Unit tests cover format/parse/round-trip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: search_files reports a requested file's real status, not "not attached" search_files filtered the store down to readyFiles() before validating a requested `file`, so searching an attached-but-not-ready file (indexing / errored / locked / unavailable) while another file was ready returned "No attached file named X" — even though it is attached. Factor read_file's status reporting into a shared notReadyMessage() and have search_files report the same accurate status before searching the ready subset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: clear attached files on new/loaded chat in the non-session global chat saveAndClear() (the "New chat" button) and loadPastChat() left attachedFiles intact. In an AI session that's intended — files are session-scoped and persist across conversations. But the ephemeral global side-panel chat has no session, so the next, unrelated conversation still got the previous file roster injected and could read_file/search_files against it. Clear attachments on both transitions when `!isSessionChat`; sessions keep them. Adds a lifecycle regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep an empty folder linked when regranting access after reload regrantLocked() dropped the locked placeholder unconditionally after #expandFolder. If the regranted folder was empty (or all-binary), #expandFolder's #ensureFolderRow no-op'd (the locked placeholder still existed), so dropping it removed the only handle-bearing row — unlinking the folder and stopping future refreshFolders from ever seeing files added back. Re-ensure a ready placeholder after dropping the locked one. Adds a regression test for the empty-regrant path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: round-trip @-mentions of filenames containing a closing bracket The bracketed mention form `@[name]` broke when the name contained a `]` (e.g. `notes ] draft.md`): the regex stopped at the first `]` and mentionTitle resolved the wrong name, so it wouldn't highlight. Escape `\` and `]` when bracketing, match escaped chars in MENTION_RE, and unescape in mentionTitle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: highlight @-mentions of filenames with HTML-sensitive / special chars getHighlightedText() escapes the textarea value to HTML before parsing mentions, then looked the parsed title up against raw attached names — so a file like `R&D notes.md` (escaped to `R&D notes.md`) never matched and wasn't highlighted. Also, names with chars outside the bare set (`<`, `>`, `&`, parens, …) weren't bracketed, so the bare regex truncated them. Now formatMention brackets any non-bare-safe name, and the highlighter HTML-unescapes the parsed title before the store lookup. Verified in a real browser with `R&D notes.md`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: report the real reason search_files has no readable targets When attachments existed but readyFiles() was empty, search_files always told the model "still being indexed, try again shortly". That's wrong for the placeholder states this PR introduces: an empty or binary-only linked folder leaves only a filtered-out `ready` placeholder, and a locked/unavailable restored folder exposes no readable children. Now the message reflects the actual state — no searchable text, restore access, or re-link — and only says "indexing" when something is. Adds a focused fileTools test for the empty-ready states. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
346cc30e2d |
chore(main): release 1.734.0 (#9691)
* chore(main): release 1.734.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.734.0 |
||
|
|
b0973c3023 |
hide primary storage row until added in workspace storage settings (#9692)
* feat(frontend): hide primary storage row until added in workspace storage settings Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): red border on empty storage resource picker Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): allow deleting primary storage when no secondary storages exist Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): disable storage save when a row is missing its resource Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3ebf24359d | feat: ducklake materialization for data pipelines (#9689) | ||
|
|
09a80040ca |
fix(frontend): clear branch step state when switching outer loop iterations (#9650)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d5388da953 |
chore(main): release 1.733.1 (#9685)
* chore(main): release 1.733.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.733.1 |
||
|
|
119d94601d |
feat(jobs): auto-grant approvers a run-detail view link on the approval page (#9686)
* feat(jobs): auto-grant approvers a run-detail view link on the approval page Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(approval): show a clear run-details button for logged-in workspace members Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(approval): build run-details link from route params, not undefined job getJob is fire-and-forget on the new approval page and is denied for an approver who lacks direct run read access — the exact case this link serves — leaving job undefined and producing /run/undefined. page.params.job is the flow id the view_token is minted for. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6aa9e515f7 |
de-flake asset-dispatch debounce test by reading settings from DB (#9687)
The debounce assertion resolved the dispatched job's debounce window through `prefetch_cached_from_handle`, which goes through the process-global runnable-settings cache (shared by every test running concurrently in the binary) and its tempdir-backed file I/O. Read the persisted rows directly from the test's isolated DB instead, removing that cross-test coupling and extra I/O from the assertion path. Still validates the full wiring (handle -> runnable_settings -> debouncing_settings). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
960c55e1f3 |
preserve URL filter state on page refresh (#9680)
* fix(home): preserve URL filter state on page refresh ListFilters.loadFilterFromUrl() runs synchronously at script init and sets ownerFilter via binding. When $workspaceStore resolves asynchronously after mount it triggered the $effect that resets ownerFilter, wiping the URL-loaded filter before the user saw any results. Skip the first $workspaceStore resolution using the same firstRun guard pattern already used in this file (firstWorkspaceRun). Workspace switches still correctly clear the filter. Fixes #9624 * refactor(home): reset filters on workspace change instead of first-run guard Track the previous workspace value and clear filters only when it actually changes, rather than skipping the first $workspaceStore resolution. Encodes the real invariant (reset on change) without depending on child/parent init ordering, and preserves URL-loaded filters on initial mount by construction. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Ruben Fiszel <ruben@windmill.dev> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c1f31c0e47 |
fix(backend): validate ansible vault_id entries before config generation (#9681)
* fix(backend): validate ansible vault_id entries before config generation Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(backend): cover ansible.cfg write-boundary vault_id validation Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c39ee07c0b |
fix: validate websocket trigger urls and gate trigger test route (#9682)
* fix: validate websocket trigger urls and gate trigger test route Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: clarify validate_websocket_url_for_ssrf call sites Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fb44fe7af2 |
fix: require super admin for object storage config test endpoint (#9683)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1be4df9acb |
fix(frontend): group live pipeline runs in the activity panel (#9684)
* fix(frontend): refetch pipeline dispatch edges on live runs so cascades group The activity panel groups runs into cascades by connected components of the dispatch-edge graph, but edges came only from the one-shot history preload (refetched on workspace/folder/days change). dispatch_event rows are written server-side when a producer completes, so a run launched live had its producer and freshly-dispatched children appear as live poll events with no connecting edge — they rendered as separate ungrouped rows instead of one cascade. Add an edges-only refetch and trigger it whenever the live poll's event id-set changes, so live cascades converge to grouped like historic/scheduled ones. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): address review nits on live dispatch-edge refetch - Sequence same-scope edges-only refetches with a monotonic edgeSeq so a slower earlier response can't overwrite a newer one mid-cascade (the gen counter only guards scope changes). - Condense the duplicated edge-refetch rationale: keep the canonical "why" in loadEdges, trim the page effect comment to its trigger/loop invariant. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b2ce475fc3 |
chore(main): release 1.733.0 (#9677)
* chore(main): release 1.733.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.733.0 |
||
|
|
5970510cf6 |
perf(backend): gate asset producer-change event on write-set changes (#9672)
* perf(backend): only emit asset producer-change event on write-set changes Data Pipelines (#9193) made every script deploy emit a `notify_asset_producer_change` event: `clear_static_asset_usage` inserted into `notify_event` unconditionally on every clear, and the per-asset insert path emitted nothing. So a plain script with no assets — the overwhelming majority of deploys — wrote a `notify_event` row that made every worker drop its `ASSET_PRODUCER_WRITES_CACHE` entry instance-wide, needlessly thrashing the cache the feature added. That cache only tracks script rows with write access (`usage_access_type IN ('w','rw')`), so a deploy changes it only when the script gains or loses a write producer. Gate the event on exactly that: - `clear_static_asset_usage` / `clear_static_asset_usage_by_script_hash` emit only when the delete removed a 'w'/'rw' row (via `RETURNING`). - `insert_static_asset_usage` emits only when it actually inserts a 'w'/'rw' script row (no-op `ON CONFLICT`, read-only, and flow usage stay silent). Plain, read-only, and flow deploys now emit nothing; producer-changing deploys still invalidate, atomically and visible-only-on-commit as before. Adds a test asserting the emit/no-emit matrix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(backend): dedup producer-change notify on write-asset redeploys Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(backend): derive replace write-set from persisted rows; document auth contract Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(backend): correct replace_static_asset_usage call-site comment Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6833a554ae |
fix: allow users to always discard their own drafts without write permission (#9659)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
4296a6ae1f |
feat(ai-chat): cap read_app_file + search_app grep tool to bound context in large raw apps (#9653)
* docs: add global AI chat context-optimization plan for raw apps Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai-evals): add global raw-app debugging cases on a large fixture Adds a ~20-file analytics_dashboard raw-app fixture (incl. a 5k-line data module and a planted wrong-totals bug), two global cases (read-heavy debug + small-edit baseline), app-seed support in the mock backend, directory-fixture loading, and a decorateHelpers seam so read-dedupe is measurable. Records tokenUsage for before/ after comparison of the read-tool optimization. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): cap and dedupe read_app_file to bound context in large apps read_app_file now defaults to a head slice (1500 lines / 50k chars) with offset/ limit to page further, and skips resending a file whose earlier read is still in context (per-conversation ledger keyed off the originating tool-call id, so it self-heals after compaction). Bounds the file-content portion of global-chat context when working in large raw apps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai-evals): add read-heavy raw-app debug case (large data module) global-test31 induces the model to inspect the 5k-line seedData module, exercising the read_app_file cap/offset path. Baseline ~262k tokens vs ~200k with the cap+dedupe change (-24%). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: record A+B benchmark results and fixed-overhead finding Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): clearer read_app_file past-EOF message + unit tests for cap/dedupe Addresses local-review nits: out-of-range offset now reports 'offset N is past the end of the file' instead of a backwards 'lines 11-10' label; adds unit coverage for the slicing (line cap, offset/limit window, char budget, past-EOF) and re-read dedupe (hit + miss-when-not-retained). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): char-level paging + per-range dedupe for read_app_file Adds char_offset/char_limit so minified/long-line files can be paged within a line window, keys the re-read ledger by range (so reading different ranges no longer collides), and dedupes on the full-file hash (a cached range stub is invalidated when any byte of the file changes, not just the returned range). Tests updated for the char-slice behavior plus single-line capping, char paging, and out-of-window change detection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai-chat): add read_app_file context micro-benchmark + re-read eval case Adds a deterministic micro-benchmark (no LLM) that drives read_app_file through a realistic big-project read pattern (large file, re-read, minified bundle, paging) and asserts the cap+dedupe cut returned context >50% vs the old whole-file behavior — isolating the feature's effect from model nondeterminism and guarding against silent weakening. Adds global-test32, a cross-file consistency investigation that revisits overlapping files so re-read dedupe is exercised in a real run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai-evals): clarify test32 measures the read cap, not dedupe Verified: sonnet and haiku both read each file once per conversation and retain it, so test32 never triggers read_app_file re-read dedupe. Dedupe is measured deterministically by the micro-benchmark instead. Comment corrected to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ai-chat): drop read_app_file re-read dedupe, ship the cap only Benchmarking showed the per-conversation re-read dedupe never fires in practice: across sonnet/opus/gpt-5.5/haiku, every model reads each file once per conversation and keeps it in context (0 within-conversation re-reads). It was a correct but unused guard, so this removes the ledger, full-file hash, retention predicate, the AIChatManager wiring, and the eval decorateHelpers seam — keeping the read cap + offset/limit/char paging (A), which is the lever that actually bounds context. The micro-benchmark is now cap-only; test32 is kept as a multi-file read-load case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): add search_app grep tool for global raw-app chat (experimental) Client-side grep over a raw app's frontend files and inline runnables (literal, case-insensitive, optional file_glob/context_lines/max_matches, head-capped). Completes the list -> search -> ranged-read triad. Includes the eval A/B gate (WMILL_AI_EVAL_DISABLE_SEARCH_APP), unit tests + micro-benchmark, and a find-all-usages eval case (global-test33). Experimental: A/B benchmarking shows it is not an unconditional win — it helps on find-all-usages but adds agentic iterations on navigable apps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai-evals): accept search_app as a valid file-inspection tool in raw-app cases Add requiredToolsAnyOf alternatives-group to ToolValidationSpec and switch global-test29..32 to it so a model that locates files via search_app instead of read_app_file no longer false-fails the tool assertion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: remove stale ai-chat context-optimization planning doc Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ai-chat): drop read_app_file char paging for a hard char cap The char_offset/char_limit params guarded minified files (a single line over the char budget) but were effectively unused in benchmarks. Remove them and the in-window char paging; keep the hard 50k-char budget and, when a read hits it, tell the model to narrow the line limit (or treat the file as unreadable if a single line exceeds the budget). Proper long-line handling is left as a TODO. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ai-chat): bake search_app context to 1 line, clarify query is literal Drop the context_lines param (models varied it to little effect) for a fixed SEARCH_APP_CONTEXT_LINES=1, and cap on matching lines instead of pushed rows so max_matches stays accurate with context always on. Sharpen the query description to state it is a literal (non-regex) substring and to suggest the call form (e.g. formatCurrency() to hit call sites and skip formatCurrencyPrecise. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ai-chat): widen baked search_app context to 2 lines Models that set the old context_lines param leaned to 2; match the lean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): count every file with a match in search_app header Move fileHadMatch ahead of the render cap so files whose matches fall past max_matches are still counted (with a regression test). Also swap the raw NUL globstar sentinel for a printable escape (the NUL bytes made core.ts read as binary to grep) and reword two comments to describe current constraints instead of drafting history. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): drop redundant input echoes from app tool results read_app_file and search_app no longer prefix results with the tool name or echo back the caller's own inputs (file path, query, file_glob) — the model already has them from the call args, and the unbounded query echo could push the search result past its output budget. Keeps the useful signals (line range, match/file counts, truncation) and the actionable advice. Also reword max_matches to 'matching lines' since it caps lines (each expands to context rows). Unit tests updated to the new format. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
924f9c7e8d |
fix(backend): strip NUL bytes from draft values on write (#9673)
draft.value is a json column (not jsonb), so a client could store a U+0000 escape in it. Any later text extraction (`->>` / `to_jsonb`) on such a value raises 22P05 "unsupported Unicode escape sequence" — one poisoned draft 500'd the whole GET /drafts/list, silently hiding the home-page "This workspace has N drafts" banner (and breaking the global drafts page). Prevent it at the source: sanitize the value in update_draft (the only path that writes client-supplied draft content) so a NUL never reaches the column. strip_json_nul does a single backslash-parity-aware byte pass that removes real NUL escapes (values and keys alike) while leaving a legitimate escaped backslash intact — O(n) with no serde_json::Value tree to allocate, important because the slow path is also hit by any value legitimately containing the text after a backslash (e.g. script source). The clean path is a single substring check. A SQL migration scrubs rows written before this, gated to genuinely-poisoned rows (a real NUL makes value::jsonb raise, distinguishing it from a legitimately escaped backslash). With the data clean, no read-side query needs to change. Tests: unit tests for the strip helper (escaped-backslash no-op, real+literal collision, odd-backslash-run parity, nested keys/values) and an integration test that POSTs a NUL-bearing draft and asserts it is stored and listed NUL-free (fails without the strip). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ab3bc97cd9 |
feat(python, windows): enable S3 to cache wheels (#5199)
* add .minio-data to .gitignore
* feat(python): make S3 cache arch specific
Current schema:
S3-Bucket
├── python_311
│ ├── wheel==1.0
│ └── wheel2==1.0
└── python_312
├── wheel==1.0
└── wheel2==1.0
New schema:
S3-Bucket
├── linux_aarch64
│ └── ...
└── linux_x86_64
├── python_311
│ ├── wheel==1.0
│ └── wheel2==1.0
└── python_312
├── wheel==1.0
└── wheel2==1.0
* remove .minio-data from .gitignore
* remove unneeded tracing::error
* feat(python, windows): enable S3 to cache wheels
* fix(python, windows): drop residual unix cfg gates on S3 cache items
Post-merge, PIPTAR_UPLOAD_CHANNEL and its call sites were already
cross-platform, but the types/functions they reference (PiptarUploadTask,
handle_piptar_uploads, pull_from_tar, OBJECT_STORE_SETTINGS) remained
unix-gated, breaking the Windows build.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
496e770264 |
Revert "fix(backend): clean up unique_ext_jwt_token on workspace deletion (#9…" (#9678)
This reverts commit
|