mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-25 00:01:55 +00:00
73c8d7f08ad55cd2323d1bb9438f00a8ac30e046
13906 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
73c8d7f08a |
fix: reject git URL fragment/query SSRF bypass (GHSA-p5cj-8cfh-mjv6) (#10120)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
360e783b1d |
chore(main): release 1.759.0 (#10108)
* chore(main): release 1.759.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.759.0 |
||
|
|
ebe31aeeac |
feat(dev-workspace): reflect existing protection rules in lock toggles (#10093)
* feat(dev-workspace): reflect existing protection rules in lock toggles When creating or attaching a dev workspace, the "block direct edits" and "prevent forking" toggles now check the root workspace's current protection rules. If a restriction is already enforced by an existing rule, its toggle is shown on but locked, with a note, instead of offering a fresh default that could misrepresent the effect. The value sent to the backend is derived so it stays consistent with what the locked toggle shows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: clarify fail-open comment on dev-workspace lock toggles Reword the protection-rule fetch comment so the fallback path isn't misread as dropping protection: a failed fetch falls back to the editable default-on toggle, and any real rule still enforces server-side. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(dev-workspace): lock protection toggles until rules load The lock toggles derived alreadyBlocks* from an async fetch, so during the load window (and the first frame before loading flips) they were editable and the effective value could be false. A user could turn a lock off and submit before an existing rule was detected, omitting the reserved rule and silently leaving prod unprotected once that existing rule was later removed. Treat "rules not yet known" (loading || current === undefined) the same as "already enforced": lock the toggle on and keep the effective value true during that window, so the request can never submit false before the fetch resolves. Submission stays available (a hung fetch degrades to over-protection, not a blocked form). Also fixes the stale-value flash when switching base workspace. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(dev-workspace): honor rule bypasses and guard stale protection fetches Two issues in the protection-rule awareness for the dev-workspace lock toggles: - Bypassable rules became unconditional locks. alreadyBlocks* used isRuleActiveInRulesets, which ignores bypass_users/bypass_groups, and forced the request flag to true. The reserved dev_workspace_lock rule is created with empty bypass lists, so layering it over an existing rule that let specific users through revoked their deploy/forking access. Switch to isRuleUnconditionallyActiveInRulesets so a toggle is only shown as already enforced (locked) when an existing rule has no bypasses; a bypassable rule stays editable, making the lock the user's explicit choice. - A stale protection fetch could apply another base's rules. The generated client can't take an abort signal, so a delayed response for a previous base could overwrite the newly selected one. Tag each result with its workspace and only trust a result matching the current base; also throw AbortError from a superseded fetch so it can't overwrite current. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: condense protection helper comment to four lines Trim the isRuleUnconditionallyActiveInRulesets doc comment to satisfy the AGENTS.md ≤4-line comment rule. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(dev-workspace): align already-enforced note under the toggle label The note used ml-8, landing under the toggle switch rather than aligned with the switch edge or the label, so it read as floating. Bump to ml-11 so it lines up under the label as helper text for that toggle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
cab3430e64 |
ci: link backend integration tests with mold to fix OOM (exit 143) (#10103)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
91b5a10504 |
ci: pin cpina/github-action-push-to-another-repository to a full commit SHA (#10119)
go_on_release.yml referenced this third-party action by the mutable @devel branch in the step that holds secrets.DENO_PAT (a write-scoped PAT used to push the generated go-client to another repo). Pinning to a full commit SHA (v1.7.3) removes the mutable-ref supply-chain exposure, consistent with the other SHA-pinned actions in the repo. |
||
|
|
eb7a2e048b | ci: cap build jobs and disable incremental in backend tests to prevent OOM (#10118) | ||
|
|
6d1e12d5e9 |
feat(nativets): expose the standard web-platform globals deno_web provides (#10112)
* feat(nativets): expose standard web-platform globals for bun parity Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(nativets): wire bun-present Event subclasses and add construction smoke test Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(nativets): seed performance.timeOrigin per isolate, drop broken reportError Addresses CI Codex review on #10112: - performance.timeOrigin was undefined (setTimeOrigin never called); seed it per isolate via __wmInitPerIsolate executed from create_nativets_runtime. - reportError needs a global EventTarget this runtime never installs; drop it. - reword the namespace-import comment to not describe drafting history. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(nativets): wire DOMException global + broad edge-case smoke sweep DOMException is present in bun and, more importantly, deno_web references it as a global: AbortController.abort() with no reason constructs a DOMException("...", "AbortError"), so the already-wired AbortController/ AbortSignal threw "DOMException is not defined" on abort. Surfaced by a new functional edge-case sweep (smoke_web_globals_edge_cases) that exercises every wired global for real (not just presence) — DOMException/abort, AbortSignal.timeout, EventTarget dispatch, stream tee/reader/writer, all 3 compression formats, structuredClone Map/Set/Date/circular/reject-function, performance mark/measure, MessagePort delivery — plus a check that the merged Web Crypto globals still work. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(nativets): restore arg-default smoke tests dropped in merge, drop history comments Addresses CI Codex/Pi review on the merge commit: - Merge conflict resolution (checkout --ours) dropped smoke_missing_optional_arg_uses_default and smoke_explicit_null_arg_is_preserved (added on main by #10111); restore them. - Reword edge-case-sweep comments to state the constraint, not how the gaps were found. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(nativets): give reportException a global dispatch target; wire stream reader/controller globals Addresses CI Codex review on #10112: - P1: a throwing EventTarget listener (and reportError) is routed through deno_web's reportException, which dispatches on a saved global reference. With none set, dispatchEvent threw a masking error that hid the original. Wire a dedicated EventTarget as that target so the ORIGINAL error is reported (async unhandled, matching bun). Does NOT make globalThis an EventTarget (bun's isn't either). Re-adds reportError, now functional. Regression test asserts the original error is surfaced, not a masking one. - P2: wire the stream reader/controller globals bun also exposes (ReadableStreamDefaultReader/BYOBReader, ReadableStreamDefault/ByteStreamController, ReadableStreamBYOBRequest, WritableStreamDefaultWriter/Controller, TransformStreamDefaultController) for instanceof parity; sweep verifies via real reader/writer/controller instances. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(nativets): make globalThis an EventTarget so globalThis.reportError() works Addresses follow-up CI Codex review on #10112: - P1: the prior fix saved a *separate* EventTarget as the global reference, so globalThis.reportError() still failed its receiver check (this === globalThis_) with 'Illegal invocation'. Make globalThis itself the saved reference by turning it into a functional EventTarget (setPrototypeOf to DedicatedWorkerGlobalScope + setEventTargetData + webidl brand + saveGlobalThisReference), per isolate in __wmInitPerIsolate. Both reportError(e) and globalThis.reportError(e) now surface the original error (async, matching bun) instead of throwing. New test smoke_report_error_both_call_forms covers both call forms. - P2: reword the regression-test comment to state the invariant, not the patch history. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(nativets): wire performance constructor globals for bun parity Addresses the P2 nit in the CI Codex review: bun exposes Performance, PerformanceEntry, PerformanceMark, and PerformanceMeasure as globals (deno_web exports all four), so wire them alongside the performance singleton. The edge-case sweep verifies instanceof against real mark/measure entries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(nativets): state global-wiring comment as a constraint, not patch history Addresses the P2 in the CI Codex review: reword the block comment to describe the current bun-parity constraint and the deliberate EventSource/ImageData exclusions, without narrating what was or wasn't wired before (per AGENTS.md). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
95d9ff02ee |
fix(jseval): raise QuickJS eval memory cap to 128MB with clear OOM error (#10116)
* fix(jseval): raise QuickJS eval memory cap to 128MB with clear OOM error Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(jseval): note bare null/undefined throws are absorbed into OOM bucket Addresses CI review P2 nit on map_quickjs_error. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(jseval): keep batch-rerun eval on a conservative 32MB cap; tighten OOM match Addresses CI review: eval_simple_js runs in the API process with unbounded request concurrency, so it must not inherit the raised flow-transform cap. Tighten the Exception OOM match to exact string. Reword drafting-history comments per AGENTS.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(jseval): gate OOM on InternalError kind; path-specific remediation hint Require the OOM InternalError name (not just the message) so a user throw new Error('out of memory') is not misclassified, and only suggest QUICKJS_MEMORY_LIMIT_MB on the env-tunable flow path (not the fixed-cap eval_simple_js path). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a6191e2a85 |
fix(mcp): advertise flow input variables in MCP tools (#10117)
Flow input schemas omit `required` (they carry an `order` key instead), which made `serde_json::from_str::<SchemaType>` fail in `convert_schema_to_schema_type`. The error was swallowed and callers fell back to an empty `SchemaType::default()`, so MCP flow tools advertised no inputs. Add `#[serde(default)]` to `type`, `properties`, and `required` on `SchemaType` so these schemas deserialize correctly. Scripts always include `required` and were unaffected. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e9fd4e7554 |
perf(rls): wrap session GUC reads in RLS policies for per-statement InitPlan (GIT-919) (#10110)
* perf(rls): wrap session GUC reads in RLS policies for per-statement InitPlan
RLS policies read current_setting('session.user' / 'session.groups' /
'session.pgroups' / 'session.folders_read' / 'session.folders_write')
directly inside their USING / WITH CHECK predicates. Postgres treats those
unwrapped calls as potentially row-varying and re-evaluates them once per
scanned row, on the read path of every workspace-scoped table.
The GUCs are set with SET LOCAL (set_config(..., true)) in
set_session_context(), so they are constant for the duration of a statement.
Wrapping each session-derived subexpression in a scalar sub-select lets the
planner hoist it to a one-time InitPlan (evaluated once per statement, reused
for every row) — same rows in, same rows out, N per-row GUC lookups collapse
to 1. Array-producing subexpressions keep an explicit ::text[] cast on the
sub-select so `= ANY (...)` / `?|` stay in their array-operand form rather
than being reparsed as a row-returning subquery.
The consolidating migration recreates every existing policy (across ~30 prior
migrations) whose predicate reads a session GUC, by deparsing the current
predicate and substituting the wrapped forms; the down migration is the exact
inverse (byte-identical round-trip). The adding-a-trigger skill documents the
wrapped form so new trigger tables inherit it.
Surfaced by pgrls (PERF001).
Fixes GIT-919
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(adding-a-trigger): fix RLS example cast placement for = any context
The `= any(...)` example put the ::text[] cast inside the sub-select, which
Postgres parses as a row-returning subquery and rejects at CREATE POLICY with
`operator does not exist: text = text[]`. Move the cast outside the sub-select
(matching the migration's canonical form) so the operand stays in array form,
and note why.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
88030d0f55 |
fix(tree-view): align file indentation with sibling folders (#10115)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ba232544e7 |
feat(nativets): add Web Crypto support via deno_crypto (#10109)
The nativets in-process runtime (deno_core) exposed no Web Crypto API:
`crypto` was undefined, so scripts could not use `crypto.getRandomValues`,
`crypto.randomUUID`, or `crypto.subtle`, even though the bun runner provides
them. This closes that parity gap by registering the `deno_crypto` extension
and wiring the crypto globals onto `globalThis`.
- Pin `deno_crypto = "0.223.0"`, the sibling release of the already-pinned
deno_core 0.352 / deno_web 0.240 stack (deps: deno_core ^0.352,
deno_web ^0.240, deno_error =0.6.1), so the rest of the deno stack is
untouched.
- Register `deno_crypto::init(None)` after `deno_web` in both the snapshot
(build.rs) and the runtime (lib.rs) extension lists, keeping the snapshot a
prefix of the runtime list. deno_crypto declares deps = [deno_webidl,
deno_web], which the position satisfies.
- Import `ext:deno_crypto/00_crypto.js` in runtime.js and assign
`crypto` / `Crypto` / `CryptoKey` / `SubtleCrypto` to `globalThis`.
- Add the `smoke_web_crypto` opt-in smoke test asserting the UUIDv4 shape of
`randomUUID`, a non-zero `getRandomValues` fill, and the known
SHA-256("abc") vector via `subtle.digest`.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
4917f79935 |
fix(mcp): let MCP tokens call preview run tools (jobs:run scope) — Fixes GIT-920 (#10107)
* fix(mcp): let MCP tokens call preview run tools (jobs:run scope) The MCP proxy mints an internal JWT scoped to exactly `scope_for_route` for the endpoint it forwards to. For preview run routes (`run/preview`, `run/preview_bundle`, `run/preview_flow`, `run_wait_result/preview`, `run_wait_result/preview_flow`), `determine_kind_from_route` matched the `SCRIPT_JOBS` prefix `jobs/run_wait_result/p` (because "preview" starts with "p") and derived `jobs:run:scripts`. But the preview handlers run arbitrary request-supplied code with no deployed path and require the broad `jobs:run` scope, so `jobs:run:scripts` was rejected with 403 "Required scope: jobs:run". Preview/bundle routes now carry no runnable kind, so the derived scope is the broad `jobs:run` the handlers expect. This also aligns the route-level access check with the handler check for these routes. Fixes GIT-920 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(mcp): anchor preview-route match to endpoint segment Address CI review: `route_path.contains("preview")` also matched by-path runs of a deployed runnable whose path contains "preview" (e.g. `jobs/run_wait_result/p/f/team/preview_report`). Since determine_kind_from_route also feeds check_route_access, such a route would derive the broad `jobs:run` and reject a legitimately kind-scoped `jobs:run:scripts:*`/`jobs:run:flows:*` token with 403. Anchor the exception to the actual preview endpoints (`jobs/run/preview*`, `jobs/run_wait_result/preview*`) so by-path runs keep their kind. Add regression tests for preview-named by-path paths, and trim the comments per AGENTS.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ba7f9c065f |
fix(nativets): apply parameter defaults for missing args instead of null (#10111)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
abbab4d423 |
security(docker): apt-get upgrade base OS in all runtime base stages (#10114)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4d0dee8d39 |
make Caddy bind tolerate an unset ADDRESS on caddy >= 2.9 (#10113)
Follow-up to GIT-903 / PR #10106. That PR migrated the caddy-l4 image to mholt's native `layer4` Caddyfile support, which required bumping the Caddy base image to 2.11.4. End-to-end testing (running the image and proxying real traffic, not just `caddy adapt`) revealed that caddy >= 2.9 changed how `bind` treats an empty argument. The shipped Caddyfile has `bind {$ADDRESS}` inside the `{$BASE_URL}` site, and docker-compose leaves ADDRESS unset -- the default self-host case. On 2.11.4 the empty `bind` makes Caddy drop the entire `{$BASE_URL}` site, so the container listens only on :25 (layer4) and the :80 HTTP reverse proxy to windmill_server silently disappears. config-only checks (adapt/validate/boot) pass, so only real traffic surfaces it. Fix in the Caddyfile rather than downgrading Caddy (which would reintroduce known CVEs on an internet-facing proxy): default the bind to all interfaces when ADDRESS is unset via `bind {$ADDRESS:0.0.0.0 ::}`. When ADDRESS is set it is honored unchanged; when unset the site binds IPv4 + IPv6, matching the pre-2.9 behavior. Verified on the caddy:2.11.4 image with a mock windmill_server backend (HTTP :8000 + layer4 echo :2525): - ADDRESS unset -> :80 and :25 both bind; HTTP and layer4 both proxy - ADDRESS=0.0.0.0 -> same - ADDRESS=127.0.0.1 -> HTTP site binds 127.0.0.1:80 (knob preserved) Fixes GIT-903 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
770ac2be9e |
fix(self-host): resolve caddy-l4 "unrecognized global option: layer4" error (#10106)
The self-hosted Caddy image relied on the abandoned
RussellLuo/caddy-ext/layer4 shim to provide the `layer4` Caddyfile
global option, alongside an old (May 2024) pin of mholt/caddy-l4 that
predated native Caddyfile support. This combination is fragile:
- If the image is ever built without the RussellLuo shim, the `layer4`
global option disappears and Caddy fails with
"unrecognized global option: layer4" — the reported bug.
- Bumping mholt/caddy-l4 to any version with native Caddyfile support
makes both modules register `layer4`, panicking at startup with
"global option 'layer4' already registered".
mholt/caddy-l4 now natively registers the `layer4` global option, so
drop the RussellLuo dependency entirely and switch the Caddyfile to the
native `route { proxy { upstream ... } }` syntax. The adapted layer4
JSON is byte-identical to the previous output, so runtime behavior is
unchanged.
Also bump the Caddy base image to 2.11.4 (required by current
caddy-l4) and add a path-filtered push trigger so the published
`:latest` image is rebuilt whenever the Caddy Dockerfile changes,
instead of only on manual dispatch (which is how `:latest` drifted out
of sync with the checked-in Caddyfile in the first place).
Fixes GIT-903
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
65d6f477ab |
chore(main): release 1.758.0 (#10084)
* chore(main): release 1.758.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.758.0 |
||
|
|
af3e3fe667 |
fix(ai-chat): size AI-created flow notes to fit their text (#10091)
* fix(ai-chat): size AI-created flow notes to fit their text Free notes created via the flow AI chat omit `size` (the tool prompt tells the model to let the editor size them). validateFlowNotes seeded a fixed 275x60 box, but free notes never grow to fit content, so multi-line markdown overflowed the box. Estimate height from the text instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): stack auto-placed flow notes by height to avoid overlap Auto-placed free notes were staggered by a fixed index*84px step, but notes can now be up to 600px tall, so consecutive generated notes overlapped. Track a running y-cursor and advance it by each note's real height. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): advance note stack cursor past preserved column notes A round-tripped note keeps its existing auto-column geometry ({-375, y}); the stack cursor ignored it, so a newly added geometry-less note landed on top. Preserved notes overlapping the auto-stack column now advance the cursor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(ai-chat): trim estimateFreeNoteSize comment per AGENTS.md Keep only the non-obvious fixed-height renderer constraint; drop the implementation narration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7ebfad382a |
feat(ai-agent): give tools a real description instead of the tool name (#10083)
* feat(ai-agent): use a real tool description instead of the tool name Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-agent): render tool-name error full width and hoist it above the description Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-agent): make tool description field hug its content so a single line is vertically centered Add an optional minHeight param to the autosize action (default unchanged at 30px) and pass minHeight 0 for the tool description so an empty/one-line field no longer reserves the 30px floor and leaves dead space below the text. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(ai-agent): regenerate OpenFlow-derived prompts, CLI guidance, and copilot zod schema for tool description Fixes the check-freshness CI failure (system_prompts + skills.gen.ts) and makes the flow copilot's openFlow.json / openFlowZod.gen.ts aware of the new AgentTool.description field so AI-authored tools can set it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f3cd5d9f7f |
fix(sessions): pending-draft debounce follow-ups (delete-cancel, keystroke de-transient, teardown count) (#10087)
* fix(sessions): cancel pending draft-prompt flush on delete deleteSession removed the record from memory and IndexedDB but left the debounced draft-prompt flush timer running; it would fire afterward and persistTouched the deleted session back into IndexedDB, resurrecting a draft deleted inside the 400ms window on the next reload. Clear the per-session timer in deleteSession. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): de-transient drafts on keystroke; count pending in workspace teardown Two follow-ups to the pending-sessions feature (#10076), surfaced by codex review: - setSessionDraftPrompt clears `transient` synchronously so a draft typed into is no longer treated as a reusable blank by createSession. Previously the flag only cleared 400ms later via the debounced flush, so pressing `+` right after typing reopened the same draft instead of spawning a second pending session. Only the IndexedDB write stays debounced. - countSessionsForWorkspace counts on `workspace_id ?? pending_workspace_id`, so the archive/delete confirmation includes persisted unsent drafts, matching reconcileSessionsLifecycle which tears them down alongside committed sessions. Adds regression tests that drive the real keystroke transition and the pending draft count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): reuse-guard on draftPrompt, not a synchronous transient clear Addresses codex-review P1 on #10087: the previous approach cleared `transient` synchronously on a keystroke to stop createSession reusing a just-typed draft. But `transient` also means "in-memory only, not yet in IndexedDB" — clearing it before the 400ms flush left the draft in neither bucket, so a reconcile landing inside the window (hydrateSessions rebuilds the list as in-memory-transients + DB rows) dropped the unsaved draft and dangled currentSessionId. Separate the two concepts instead: keep `transient` as pure persistence state (the draft survives hydration), and define a reusable blank as `transient && !draftPrompt`. createSession's reuse probe and its non-reuse drop both key on isReusableBlank, so a typed-but-unflushed draft is neither reused nor discarded, and setSessionDraftPrompt no longer touches `transient`. Adds a regression test that interleaves a first-touch debounce with reconcile and asserts the draft stays in memory (and currentSessionId intact); updates the keystroke test to the real transient-preserving transition. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): treat a typed-then-erased draft as touched, not a reusable blank Addresses codex-review P2 on #10087. isReusableBlank used `!s.draftPrompt`, so a draft typed into then erased back to '' (draftPrompt === '', flush still pending) was classed as a reusable blank: pressing `+` within 400ms reused it, but after the flush cleared `transient` the same `+` created a new session — inconsistent across the debounce boundary, and in another family the non-reuse drop removed the draft while its pending timer later persisted it back. setSessionDraftPrompt only sets draftPrompt on a genuine edit (mount-time '' is a no-op via the equality guard), so `draftPrompt === undefined` cleanly means "never edited". Key isReusableBlank on that instead of falsiness. Adds a type-then-erase-before-`+` regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(sessions): trim new comment blocks to AGENTS.md 4-line limit Addresses codex-review P2 on #10087: condense the setSessionDraftPrompt, countSessionsForWorkspace, and isReusableBlank comment blocks to <=4 lines per the AGENTS.md rule. Comment-only, no logic change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(sessions): trim last two comment blocks to AGENTS.md 4-line limit Follow-up to codex/pi P2 nits on #10087: condense the keystroke-transition test comment (5→4 lines) and the countSessionsForWorkspace comment (→3 lines). All new comment blocks in the PR are now ≤4 lines. Comment-only, no logic change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4edffeb84b |
fix(mcp): align script auto_kind filter with scripts list API (#10098)
The MCP `get_items` script filter used `auto_kind IS NULL`, which excluded every script with a non-null `auto_kind` (pipeline, test, WAC, ...). These are valid runnable scripts and should surface as MCP tools. Switch to the deny-list `(auto_kind IS NULL OR auto_kind <> 'lib')`, matching the scripts list API (windmill-api-scripts). Only library scripts (no main function) are excluded; pipeline/test/WAC and any future auto_kind values are included. Fixes WIN-2190 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2c702efec0 |
fix(frontend): stop spurious raw-app reload that 404s on "Start without AI" (#10099)
* fix(frontend): stop spurious raw-app reload that 404s on "Start without AI" Creating a new raw app and clicking "Start without AI" surfaced an "App not found" toast. The page's load effect re-ran loadApp() mid-bootstrap and fetched the draft via getAppByPath before the first autosave POST had landed → 404. Root cause: the effect used the legacy run() from svelte/legacy without untrack, so loadApp()'s synchronous reactive read of the draft-hint SvelteMap (getLocalDraftHint via shouldSeedNewDraft, added in #10044) subscribed the effect. The first autosave optimistically flips that hint (#9351) before its debounced POST, re-firing the effect → spurious loadApp() → getAppByPath on a not-yet-persisted draft. Convert the block to $effect + untrack so it depends only on page.params.path / $workspaceStore, matching the sibling apps/edit and flows/edit routes. Autosave and draft persistence are unchanged; only the phantom reload is removed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(frontend): tighten untrack invariant comment to ≤4 lines Per AGENTS.md comment policy (Codex review nit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
cfc3f292ad |
fix(apps): allow setting sandbox isolation and public access before first deploy (#10085)
* fix(apps): allow enabling sandbox isolation before first deploy Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): allow setting public access mode before first deploy Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
98e6cca75d |
feat(cli): add --tag override to script and flow run/preview (#10079)
* feat: custom tags on CLI runs, show previews in default runs view Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * revert: don't include previews in default runs view Deferring the runs-view UX change; keeping only the CLI --tag work. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): forward --tag for codebase/bundle script previews The bundled-preview branch posts a multipart payload to /jobs/run/preview_bundle; --tag was only wired into the non-bundled runScriptPreview call, so codebase previews silently used the default tag. Include tag in the preview payload (backend reads preview.tag). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
32f32d9a29 |
feat(ai-chat): port flow-group and sticky-note instructions to global chat (#10090)
* feat(ai-chat): port flow-group and sticky-note instructions to global mode Global-mode AI chat inherited only the bare FlowGroup schema and had no sticky-note support, so it never proactively segmented flows into groups and could not author flow-wide notes. Flow mode carried this guidance inline in its own prompt and set_flow_json tool. Bring global mode to parity: - Enrich write_flow's `groups` description (color palette + fields) and add a `notes` field mirroring flow mode's set_flow_json. - Thread `notes` through editableFlowToDraftValue and the write_flow handler so notes reach FlowValue.value and survive the deploy round-trip. Reads and patch_flow_json already carried notes via the shared editableFlowJson helpers. - Expand getFlowInstructions with the groups/notes organizing guidance (strongly-recommended proactive grouping, color palette, when-to-use-which) and mention notes in the write/read/compact-view/structural-edit bullets. Add a write_flow -> read_workspace_item notes round-trip test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ai-chat): trim write_flow groups/notes schema descriptions The write_flow tool schema is re-sent every chat loop iteration, so the verbose groups/notes descriptions were a per-iteration token tax that duplicated the on-demand getFlowInstructions() prose. Trim the .describe() calls to the correctness-critical bits (color palette, type "free", null semantics) and point to get_instructions for the full field reference, which getFlowInstructions() already carries. Addresses CI review feedback (Claude + Pi). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
89bb63cff5 |
ci: drop debuginfo in backend integration tests to prevent runner OOM (#10088)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
eff9076e91 |
fix(sessions): reopen script test panel when preview goes full screen (#10082)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f2869d8c1a |
fix(apps): load themes when selecting the Resources → Theme tab (#10086)
The Resources page dispatches per-tab data loads from the Tabs `on:selected` handler and `reload()`, but both only handled `cache` and `states` — selecting the Theme tab never called `loadTheme()`, so `themeResources` stayed undefined and the tab rendered empty even though app themes existed. The reload `$effect` reads `tab` inside `untrack`, so it didn't re-fire on tab change either (only a filter or workspace change did, which is why typing in the filter "fixed" it). Add the missing `theme` branch in both places. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
15391f6399 |
perf(runs): index-bound batch re-run selection with a lossless completed_at bound (WIN-2168) (#10074)
"Re-run all jobs matching filters" selects completed jobs via list_filtered_uuids windowed by started_before/started_after (the timeframe). v2_job_completed has no index on started_at (only completed_at), so that filter alone forces a workspace-wide seq scan — a query observed at ~48s on a large instance. started_at >= minTs implies completed_at >= minTs (a job completes at/after it starts), so adding completedAfter = minTs is a lossless bound: it drops no row the started_at window keeps, but lets the (workspace_id, completed_at DESC) index start the scan at the window's lower edge instead of scanning the whole table. The selected cohort is unchanged (started_at stays the exact filter); this is purely a plan improvement. EXPLAIN: seq scan -> completed_at index scan. Not completedBefore: a job can start in-window but finish after maxTs, and bounding completed_at above would drop it. Scoped to re-run; batch cancel (v2_job_queue, small) is untouched. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
bfcec7e8ac |
feat(sessions): support many pending sessions persisted in IndexedDB (#10076)
* feat(sessions): support many pending sessions persisted in IndexedDB Allow several unsent AI sessions to be set up in parallel. Split the transient flag into "in-memory, not yet persisted" (unsent is derived from workspace_id), persist a pending session to IndexedDB on first touch with its own draftPrompt, show pending sessions in the sidebar under the family filter, and reconcile them by pending_workspace_id. The + button reuses the untouched draft in the active family so idle clicks don't pile blank entries; touching one spawns a fresh blank. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(sessions): focus composer when + reuses the untouched draft When there are no pending changes, `+` reuses the active family's untouched draft instead of creating a new session (unchanged). But when the reused draft is the one already on screen, currentSessionId doesn't change, so nothing navigated and the click gave no feedback. Bump a composerFocusRequest nonce in the reuse branch and have SessionWrapper's focus effect depend on it, so the composer re-focuses and the user can type right away. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): per-session debounce for draft prompt flush A single module-level flush timer let a keystroke in one pending draft cancel a sibling draft's pending first-touch flush, so the earlier draft was never written and its typed prompt vanished on reload. Key the debounce per session so parallel drafts persist independently. Also collapse the touch rationale repeated across the preview-tab/collapse/size setters onto persistTouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1ffe5a1075 |
chore(main): release 1.757.0 (#10080)
* chore(main): release 1.757.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.757.0 |
||
|
|
851e30914e |
feat(saml): add ALLOW_PRIVATE_SAML_METADATA_URLS SSRF bypass (#10077)
* feat(saml): add ALLOW_PRIVATE_SAML_METADATA_URLS SSRF bypass Introduce the ALLOW_PRIVATE_SAML_METADATA_URLS env var and its allow_private_saml_metadata_urls() helper, mirroring the existing ALLOW_PRIVATE_MCP_SERVER_URLS opt-out. This lets self-hosted deployments with internal SAML IdPs (private IPs, no public DNS) skip the metadata-URL SSRF check that otherwise blocks server startup. The companion EE change (saml_ee.rs) consumes the helper to gate the validate_url_for_ssrf() call and additionally treats a cleared (empty/whitespace-only) SAML_METADATA setting as no SAML configured. Fixes WIN-2169 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(saml): surface opt-in hint and record SSRF control in threat model Add saml_ssrf_error_message() so private-IdP metadata URL rejections point to ALLOW_PRIVATE_SAML_METADATA_URLS (mirroring the MCP helper), with a unit test. Record the new SSRF opt-in under T2 in THREAT_MODEL.md, and bump the EE ref for the companion saml_ee.rs change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(saml): add validate_saml_metadata_url with opt-in unit tests Factor the SAML metadata SSRF gating into validate_saml_metadata_url() (mirroring validate_mcp_server_url) so the private-URL opt-in branch is unit-tested at the ssrf layer: blocks private by default, allows on true/1, and keeps scheme/host syntax guards when the opt-in is on. Bump the EE ref for the companion saml_ee.rs change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 394ad23242de429aef4074cc1dc28867dac95870 This commit updates the EE repository reference after PR #659 was merged in windmill-ee-private. Previous ee-repo-ref: 86da208c5aef2570568e18c7ab98f4d58adeec18 New ee-repo-ref: 394ad23242de429aef4074cc1dc28867dac95870 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> |
||
|
|
46f07ab032 |
docs: mandate local-review-codex alongside local-review before PRs (#10078)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b4c834f3cd |
ci: run Codex/Pi review on fork PRs when a maintainer triggers it (#10069)
* ci: run Codex review on fork PRs when a maintainer triggers it The fork skip in codex-pr-review.yml unconditionally bailed on cross-repository PRs, so even a maintainer's /codex or /review comment (routed through pr-review-commands.yml via workflow_call, gated by check-write-access) skipped external PRs. Gate the skip on the automatic pull_request trigger only, detected via an empty INPUT_PR_NUMBER (the metadata step already branches on this at the same step). The workflow_call path now reviews fork PRs; the auto pull_request trigger still skips them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: run Pi review on fork PRs when a maintainer triggers it Apply the same fork-skip gating as the Codex review: skip fork PRs only on the automatic pull_request trigger (empty INPUT_PR_NUMBER), so a maintainer's /pi or /review comment (workflow_call, gated by check-write-access) reviews external PRs. Claude's pr-ready-review.yml needs no change: it has no fork skip, checks out main (not the fork ref), and reviews via gh pr diff/view with a restricted tool allowlist, so it already handles fork PRs on the command path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: harden fork-review path against secret exfiltration Addresses the CI review of the fork-review enablement. On the fork path (maintainer-triggered workflow_call for a cross-repository PR), the reviewer ran an autonomous agent over the attacker-controlled merge checkout with the EE token present, full-access sandbox, and the review prompt itself read from that untrusted checkout — so a malicious fork could rewrite the reviewer's own instructions to exfiltrate secrets. For fork PRs only (detected via the is_fork step output): - withhold WINDMILL_EE_PRIVATE_ACCESS: skip the EE access/checkout/ substitution steps, so the private-repo token is never in the env. - read REVIEW.md and the prompt file from the trusted base ref (git show origin/<base>:...) instead of the merge checkout. - restrict the agent: Codex runs with -s workspace-write (network off) instead of danger-full-access; Pi drops the bash tool. Non-fork PRs are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: redact provider credentials from fork review comments The model call needs the provider credential in its environment/config, so a network-disabled sandbox alone can't stop a prompt-injected fork review from reading the key (Codex: $HOME/.codex/auth.json; Pi: /proc/self/environ) and emitting it in the final message, which both workflows post verbatim. GitHub Actions log masking does not cover comments posted via the API. Strip the known credential values (OpenAI key + raw Codex auth JSON and its nested tokens; DeepSeek key) from the review body before posting, closing the comment as an exfiltration channel. Applied unconditionally since a credential should never appear in a review comment regardless of trigger. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: don't persist github.token in fork review checkout actions/checkout writes github.token into .git/config (http.extraheader) by default. The review agent can read the checked-out tree, so on the fork path a prompt injection could exfiltrate that token (issue/PR write) via .git/config — the provider-credential redaction added earlier didn't cover it. Set persist-credentials: false on the merge-ref checkout so the token is never written to disk. Safe on both paths: the only later git op is an unauthenticated fetch from the public origin, EE checkout uses its own token, and gh uses GH_TOKEN. Also redact github.token from the posted comment as defense-in-depth. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: disable Pi project-local discovery on fork reviews Pi auto-discovers and executes project-local .pi extensions (.ts/.js) at startup with DEEPSEEK_API_KEY in its environment — before the --tools allowlist applies — so a fork could add an extension that exfiltrates the key over the network, which output redaction can't catch. On the fork path (cwd is the fork checkout), pass --no-extensions to disable extension discovery, plus --no-skills/--no-prompt-templates/--no-themes/ --no-context-files so fork-controlled skills, templates, themes, and AGENTS.md/CLAUDE.md aren't auto-loaded into the reviewer's prompt as an injection vector. Non-fork behavior unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: use unguessable delimiter for untrusted PR metadata outputs The PR title/body were written to $GITHUB_OUTPUT with a fixed heredoc terminator (PR_BODY_EOF). A fork author could embed that terminator in their PR body to close the heredoc early and append their own output lines — e.g. is_fork=false, which (last-write-wins) overrides the real is_fork=true and puts fork code back on the trusted path (EE checkout + substitute_ee_code.sh with the private token, full-access agent). Generate a per-run random delimiter (128 bits from /dev/urandom) for the title and body heredocs so the terminator can't be predicted or embedded. Everything else in the block is single-line and newline-free, so this closes the injection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: set PI_OFFLINE=1 on fork Pi reviews to block package resolution --no-extensions only filters which resources are *loaded*; Pi still resolves packages declared in a fork's .pi/settings.json first, running `npm install` / the configured npmCommand and lifecycle scripts with DEEPSEEK_API_KEY in env and network available — before the extension filter applies. Set PI_OFFLINE=1 on the fork path so the resolver's installMissing() short- circuits (returns false) for every missing package, skipping all install/clone/ lifecycle execution. It gates only startup network ops (installs, helper-binary downloads), not the provider inference call, so the review still runs. Verified: a fork .pi/settings.json with a malicious npmCommand does not execute under the flag. Non-fork path unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: run fork Pi review from an isolated dir to cut off project config Root cause of the recurring fork-review exposure: Pi resolves every project config from <cwd>/.pi — settings/packages, extensions, skills, themes, prompts, SYSTEM.md, APPEND_SYSTEM.md — so running inside the fork checkout let a fork inject any of them to execute code or rewrite the reviewer's system prompt with DEEPSEEK_API_KEY in env. Per-flag opt-outs (--no-extensions, PI_OFFLINE, ...) only covered discovered vectors one at a time (SYSTEM.md wasn't covered). Discovery is cwd-based (single level, no walk-up; global fallback is the trusted runner home), so run Pi from a fresh mktemp dir where no fork .pi/* is on the path. The fork agent has no shell, so pre-compute the diff (base...head SHAs are trusted) into the context file it reads; it may still read fork files by absolute path for extra context — reads are safe, only config discovery and code execution were the risk. Outputs now use absolute workspace paths since cwd moved. The --no-* flags and PI_OFFLINE stay as belt-and-suspenders. Non-fork path unchanged. Verified: a fork .pi/SYSTEM.md sentinel is not discovered from the isolated cwd. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: keep review artifacts outside the checkout to defeat symlink writes Both workflows wrote generated files (final message, event stream, review context, prior-comments) into $GITHUB_WORKSPACE. On the fork path the merge tree is attacker-controlled, so a fork could commit any of those paths as a symlink (e.g. codex-final-message.md -> ../../_actions/actions/github-script/v7/dist/ index.js). Our write would follow it and overwrite the next action's code, which then executes with the provider credential and the write-capable GitHub token — no prompt injection required. Route every generated file through $RUNNER_TEMP, which is runner-created and outside the checkout, so no fork-committed symlink is on the path: - prior-comments.json and pr-review-context.md are written to RUNNER_TEMP; the context step reads prior-comments from there. - The agent is given the context file's absolute RUNNER_TEMP path (appended to the prompt); prompt files updated to reference it instead of a checkout- relative path. Pi (no shell on forks) gets the diff pre-computed into that context file; the isolated-cwd hardening is retained. - Codex writes -o to RUNNER_TEMP; Pi writes its events/final message there; both post steps read from RUNNER_TEMP. Non-fork behavior is functionally unchanged (trusted checkout; same review inputs, now sourced from RUNNER_TEMP). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: condense fork-review comments to the 4-line limit AGENTS.md requires each invariant stated in <=4 lines. Trim the security comments added in this branch (fork-skip rationale, output delimiter, isolated cwd, RUNNER_TEMP artifacts, credential redaction) to comply without dropping the constraint each one records. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
207ce8649c |
fix(ai-agent): don't mark repeated tool calls as failed in flow graph (#10075)
* fix(ai-agent): don't mark repeated tool calls as failed in flow graph Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai-agent): cover reporter's mixed repeated-tool-call scenario Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4f65187f9e |
chore(main): release 1.756.1 (#10072)
* chore(main): release 1.756.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.756.1 |
||
|
|
710a13a59d |
fix(apps): cover script/flow component outputs in deployed-app S3 provenance gate (#10070)
* fix(apps): cover script/flow component outputs in deployed-app S3 provenance gate Deployed apps read S3 files on-behalf of the app author for logged-in viewers (#10048). A confused-deputy guard confines those reads to files the app "produced", but the recent-production check only matched inline `appscript`/ `preview` jobs nested under the app path. Files produced by the deployed script/flow components an app is wired to run (e.g. a SQL query persisted to S3) were therefore denied "File restricted" for every viewer, admins included. Expand the provenance check to also match completed `script`/`flow`/`flowscript`/ `flownode` jobs whose `runnable_path` is one of the app's declared triggerables, and accept the author identity via `permissioned_as = on_behalf_of` (not only `created_by = caller`) so files produced on-behalf of the author are covered. Reads outside the app's declared triggerables stay denied. Adds a regression test seeding a script-kind produced file that reproduces the "File restricted" denial before the fix and passes after. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): key S3 provenance on on-behalf identity + cover flow steps (review) Addresses the CI review on the S3 provenance gate: - P1 (confused deputy): the recent-production check keyed on `created_by = caller`, so a viewer who can run a declared script/flow directly (outside the app, with un-pinned inputs) could craft a result naming an author-only key and read it back through the app as the author. Key provenance instead on the producing job's `permissioned_as` matching the on-behalf identity the download reads as (the author in author-mode); a viewer's direct run has `permissioned_as = viewer` and no longer clears the gate. Drops `created_by` from both the appscript/preview and script/flow branches, closing the same latent hole in the pre-existing inline-script branch. - P2 (dead flow-step branch): `flowscript`/`flownode` jobs have `runnable_path = <flow_path>/<step_id>`, which exact `= ANY(...)` never matched. Split script vs flow triggerable paths; flow kinds now match the flow's own job (bare path) and its step jobs via a `<flow_path>/%` prefix, bounded to declared flows. - P2 (test realism): the regression test now uses the production component-prefixed triggerable key format (`<id>:script/...`), exercises a flow-step-produced key, and asserts a viewer's own direct run of a declared script stays denied (the P1 case). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): tie deployed-app S3 provenance to an app-origination marker (review) Second CI-review round flagged that `permissioned_as` still does not prove a job was app-launched: a runnable configured with its own `on_behalf_of` makes a direct `/jobs/run` resolve `permissioned_as` to that identity (the app author), so a viewer with run access could execute a declared runnable directly, craft an S3 result, and read it back through the app. The flow-path `LIKE fp || '/%'` match also let `_`/`%` in a declared path admit unrelated flows. Introduce a real app-origination marker instead of inferring provenance: - Add `JobTriggerKind::App`; `execute_component` stamps every app-launched job with `trigger_kind = 'app'` + `trigger = <app path>`. A direct `/jobs/run` cannot set this, so it is the authoritative signal that a file was produced *by the app*. - The provenance gate's recent-production check collapses to `trigger_kind = 'app' AND trigger = <this app path>` (+ the 3h window and result containment). This drops the forgeable `created_by`/`permissioned_as`/ `runnable_path`/kind logic entirely and removes the `LIKE` wildcard issue. - Provenance is scoped to THIS app's path, so another app's jobs (even same author) do not authorize this app's reads. Regression test rewritten to the marker model: an app-produced key clears for viewer and admin; a direct run whose `permissioned_as` resolves to the author stays denied (the forgery); another app's output stays denied. Adds `app` to the OpenAPI JobTriggerKind enum. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(apps): assert execute_component stamps trigger_kind='app' at runtime Adds an end-to-end test that runs a real script component through the app runtime (`apps_u/execute_component`) and asserts the enqueued job carries the app-origination marker `trigger_kind = 'app'` + `trigger = <app path>` (not the runnable path). The provenance-gate tests seed the marker directly; this proves the runtime actually produces the exact marker the gate depends on. execute_component commits the job row and returns its id, so the assertion reads the row directly — no worker needed to run the job. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): reject trigger_kind=app for suspended-job reassignment (review) `JobTriggerKind::App` (added for the app-origination S3 marker) became a valid value for the resume/cancel suspended-trigger routes, whose handler derives the table name `<kind>_trigger`. There is no `app_trigger` table, so both endpoints would fail with a missing-relation database error (500). Reject `App` in `get_suspended_trigger` alongside webhook/schedule so it returns a clean 400. Adds a regression test asserting the reassignment route returns 400 (not 500) for trigger_kind=app. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): don't stamp app-origination marker on preview runs (review) The app-origination marker (trigger_kind='app') was stamped unconditionally, including preview mode. A preview lets a `jobs:run` caller supply arbitrary `raw_code` against ANY app path without that app's deployed policy (raw_code with no path/id skips all app authorization), so a preview returning `{"s3":"<author-only-key>"}` would forge the exact marker the S3 provenance gate trusts and read the victim app author's file. Gate the marker on `!is_preview`: only deployed, policy-checked executions are app-provenanced. Preview/editor S3 display does not rely on this marker (the editor routes reads through the force_viewer allowlist), so nothing legitimate regresses. Adds a regression test asserting a preview run's job is not stamped trigger_kind='app'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): editor-authorize preview marker + per-viewer S3 provenance isolation (review) Closes the codex P1 (preview forgery) without breaking editor preview downloads, and adds cross-viewer isolation to the provenance gate. - Preview marker now requires app write: `execute_component` stamps the app-origination marker on a preview only when the caller can EDIT that app (`require_is_writer`), instead of never stamping previews. An app editor already wields the app's author identity (they can deploy a component that reads the same file), so marking their own preview is no escalation and keeps preview-produced S3 results downloadable in the editor; a `jobs:run`-only caller who cannot edit the app still cannot forge the marker. Deployed runs are unchanged (always marked). - Per-viewer isolation: the provenance gate now also requires `j.created_by = <this caller>`. The security boundary stays the un-forgeable `trigger_kind='app'` marker; `created_by` is an additional filter ANDed under it, so it only narrows — a viewer can only download keys their OWN app runs produced, not another viewer's result. Restores the per-caller scoping #10048 had, now safe on top of the marker. Tests: preview marked iff caller can edit the app; cross-viewer isolation (another viewer's app-marked key denied, no admin bypass); direct-run and other-app keys still denied; deployed run still stamped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): require apps:write scope (not just writer ACL) to mark preview provenance (review) require_is_writer checks the user's underlying ACL but ignores token scopes, so a writer's token deliberately scoped to apps:run/apps:read/jobs:run but WITHOUT apps:write could still mark a preview and forge provenance — even though that token cannot deploy the app (update_app requires apps:write), breaking the "any marked caller can deploy equivalent code" rationale. Require BOTH apps:write:<path> scope (check_scopes) AND the writer ACL (require_is_writer) before stamping a preview's app-origination marker. Deployed runs unchanged. Adds a scope-restricted-writer token to the test (apps:run/read + jobs:run, no apps:write) and asserts its preview stays unmarked; retains the full-editor positive case and the non-editor negative case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): never app-provenance preview runs; read editor S3 as the caller (review) Simplifies the preview handling: a preview executes as the *caller* (Viewer mode), never as the author, so its results must be read back as the caller — never author-mode — and must never carry the app-origination marker. This removes the whole `require_is_writer` / `apps:write` / `can_preserve_on_behalf_of` reasoning (which was also unsound: a writer's token or session may not be able to deploy a component running as the app's on-behalf identity, so marking their preview could still escalate). - Backend: mark the app-origination marker for deployed runs only (`!is_preview`). - Frontend: `getS3File` (AppImage/AppPdf/AppDownload) now routes editor/preview reads through the viewer-scoped `job_helpers/download_s3_file` endpoint (reads as the caller), matching what DisplayResult/ParqetCsvTableRenderer already do; only a deployed app view uses the provenance-gated `apps_u` endpoint. This is the path that previously relied on marking previews, so nothing regresses. Test: a preview is never app-provenanced (owner's own preview and a non-editor's both stay unmarked). Cross-viewer isolation, deployed marking, and the reassignment guard are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): app components run on-behalf of the app, not the referenced runnable (review) Root-causes codex's on-behalf-preview finding: `execute_component` was overriding the app's resolved on-behalf identity with the referenced script/flow's OWN `on_behalf_of` (its `on_behalf_of_email`). That is wrong in the app context — the app's execution mode should govern: - A Viewer-mode app could execute a component AS the referenced runnable's on_behalf identity (privilege confusion / escalation), instead of as the viewer. - A preview would run as that identity rather than as the caller, so its S3 output could not be read back as the caller — the download-identity mismatch codex flagged. Always use the app-resolved identity (author in author-mode, caller in viewer/preview); a referenced runnable's own `on_behalf_of` no longer leaks into app execution. Direct `/jobs/run` still honors a runnable's `on_behalf_of` (unchanged). With this, previews always run as the caller, so reading editor/preview S3 as the caller (viewer-scoped `job_helpers`) is unconditionally correct. - Test: the deployed-component e2e now seeds the script with a distinct on_behalf and asserts the component job's `permissioned_as` is the app identity, not the script's. - Also reword the getS3File `configuration` param comment to describe current state only (AGENTS.md comment rule). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(apps): surface 'app' trigger kind in Runs UI; condense provenance comments (review) Addresses codex review nits: - Add `app` to `jobTriggerKinds`, `triggerIconMap` (LayoutDashboard), and `triggerDisplayNamesMap` so app-component jobs (which now carry `trigger_kind = 'app'`) are filterable in Runs and render their trigger info. - Condense the app-origination marker, on-behalf-identity, and provenance-gate comments to state each invariant once in <=4 lines at its relevant site (AGENTS.md comment rule). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
22b47c8823 |
chore(main): release 1.756.0 (#10062)
* chore(main): release 1.756.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.756.0 |
||
|
|
29f4cd4b6f |
feat(triggers): serve binary HTTP-route responses via base64 transfer encoding (#10058)
Add an opt-in `wm_content_transfer_encoding: "base64"` field to the composite result. When set (together with `wm_content_type`), result_to_response decodes the string result into raw bytes before sending it, so sync HTTP routes/webhooks can return arbitrary binary payloads (PDFs, images, ...) with any content type — not just as base64 text or via object storage. Explicit and safe: the encoding is never guessed, invalid base64 is a hard error (no silent fallback to the encoded text), an unsupported encoding is rejected, and a transfer encoding without a content type is rejected. Existing string responses are unchanged. Closes #5986 |
||
|
|
92b7f375a9 |
fix: replicate all secrets on fork when external backend is configured (#10060)
* fix: replicate all secrets on fork with external backend (WIN-2161) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test: add Azure KV fork secret-replication reproduction (WIN-2161) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * style: condense clone_variables invariant comment (WIN-2161) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test: drive real create_fork handler in Azure KV repro (WIN-2161) Replace the windmill-common test that mirrored clone_variables' loop with an end-to-end test in windmill-api-integration-tests that exercises the real migration, create_fork and variable-read endpoints against a local Azure KV emulator. Verified it fails (404 "not found in Azure Key Vault") without the fix and passes with it; unique per-run ids keep it robust to the emulator's persistent state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
5cde2d5b67 |
fix(sessions): sync AI-session preview with workspace edits + stop phantom autosave (WIN-2160) (#10061)
* fix(sessions): sync AI-session editor preview with workspace edits + stop phantom autosave (WIN-2160) Two related draft-sync fixes surfaced by the new AI sessions preview. 1. Session preview went stale after a workspace edit. A session's editor runtime cell (content store + loadedPath) outlives the sessions page: it survives toggling to workspace mode and MRU tab eviction. The shared per-user draft can change while the editor is unmounted — most visibly by editing the same item in the classic workspace editor, or from another device — but on the next mount the load early-returns on the still-set loadedPath and the preview keeps showing the pre-toggle content. Fix: invalidate the cell's loadedPath when SessionEditorTarget unmounts, so the next mount re-fetches the draft as a clean first load. This also sidesteps a Monaco model-reuse race (a force-reload that remounts the editor while the old one is still disposing renders a stale model) and prevents the outbound draft-sync from posting the stale store back (ready() stays false until the reload lands). Applies to all three editor kinds (script, flow, raw app) since they share SessionEditorTarget. 2. Opening a deployed script in the full-page editor autosaved a phantom draft with no user change. The deployed baseline carries a server-derived assets: [] that the editor's draft value never reproduces, so draftValuesEqual never matched baseline, discardIf returned false, and the settle-time write posted a no-op draft. Fix: ignore assets in the draft-vs-baseline comparison (it's derived from content, so it can't mask a real change). Fixes WIN-2160 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(sessions): condense teardown-invalidation comment to repo comment-length rule --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4783c01bff |
ci: drop debuginfo in Windows backend tests to fix disk exhaustion (WIN-2162) (#10059)
The Windows integration-test build (`cargo test --all --features …`) fills the runner's C: drive during linking. profile.dev leaves the (large) windmill workspace crates at the default debug = 2, so full debug info is emitted into every object file and embedded in each test binary — the dominant consumer of the ~63GB free on the runner. The previous split-debuginfo=off knob only suppressed the separate .pdb, leaving the embedded debug info in place; it was borderline and the Rust 1.97.0 bump (v1.755.0) pushed it over into a disk-full failure. Set CARGO_PROFILE_DEV_DEBUG=0 and CARGO_PROFILE_TEST_DEBUG=0 so no debug info is generated at all for the CI dev/test profiles. This supersedes split-debuginfo=off (no debuginfo => no .pdb, no mspdbsrv type server) and substantially shrinks the target dir. CI-only; local dev builds are unaffected. Fixes WIN-2162 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1ed7fc066b |
chore(main): release 1.755.0 (#10041)
* chore(main): release 1.755.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.755.0 |
||
|
|
ff774c46bf |
feat: add per-workspace job-retention override (#10050)
* feat: add per-workspace job-retention override (EE) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 2ba6a2a75b6fc97858b306b2c98ada481e363c10 This commit updates the EE repository reference after PR #658 was merged in windmill-ee-private. Previous ee-repo-ref: e7fb36acd813cd717bcf05f5aafbf81de271d618 New ee-repo-ref: 2ba6a2a75b6fc97858b306b2c98ada481e363c10 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> |
||
|
|
6f49a1f6a9 |
fix(docker): pin ansible tool interpreter to a persistent path (#10054)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
04eb7ddd39 |
fix: clearer errors on auto-draft save failure (WIN-2157) (#10053)
* fix: clearer errors on auto-draft save failure (WIN-2157) When an autosave draft save fails, the cloud indicator now surfaces the backend reason on hover (native title tooltip) in addition to the existing click popover, so the cause is discoverable without a click. Backend now returns a clearer, actionable message: - `require_can_write_path` distinguishes a malformed path (unrecognized namespace prefix -> BadRequest) from a genuine permission denial, and the deny message spells out where the user *can* write. - `require_owner_of_path` no longer panics with an out-of-bounds index on a malformed single-segment path (e.g. a bare `u`/`f`); it returns a clear BadRequest instead. Covered by a regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: trim narrative comment to invariant in drafts.rs (WIN-2157) Address CI review (AGENTS.md: comments record constraints, not narration, ≤4 lines): keep the malformed-path invariant, drop the motivation tail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: don't let a malformed stored draft 400 the draft listing (WIN-2157) Address CI review (P1): require_can_write_path can now return BadRequest for a malformed path, and list_drafts propagated it — so a single malformed stored draft row (the draft table has no path constraint; legacy/admin-authored rows may be malformed) would make GET /drafts/list return 400. Treat BadRequest like NotAuthorized there: the row is simply not writable. Verified e2e on EE — listing returns 200 with can_write false for the malformed rows. Also trim "unchanged"/"still" drafting-history narration from the regression test comments (P2, AGENTS.md). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: compress list_drafts comment to 4 lines (WIN-2157) Address CI review P2: keep the constraint (draft table has no path constraint) and the invariant (one malformed row must not 400 the listing) within the AGENTS.md ≤4-line limit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a89b896ce5 |
fix(frontend): mint draft path for new SDK builder items so autosave attaches (#10056)
Fixes WIN-2159 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7d02d9a1e4 |
fix(frontend): keep draft autosave alive after AI-session round-trip (#10052)
* fix(frontend): keep draft autosave alive after AI-session round-trip A UserDraft entry is shared by refcount across the components editing the same draft — notably an AI-session preview and the nav editor on either side of the Workspace<->AI Sessions toggle. The entry's autosave mirror was a $effect.root created inside whichever component first acquired it; when that component (the session preview) unmounted while the returned-to nav editor still held a refcount, the mirror stopped firing even though the entry lived on — silently killing autosave in the workspace editor for scripts, flows and (raw) apps. Move the cell out of the mirror root (so handles survive) and re-home the mirror to each new acquirer, so it is always owned by a mounted component. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(frontend): record mirror-ownership invariant on releaseEntry Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): preserve sync baseline across mirror re-home Addresses a re-home edge case (Codex review): the replacement mirror rearmed the first-write skip, so a draft edit the outgoing mirror had not yet observed (e.g. a session edit still pending at the Workspace<->AI Sessions handoff) was swallowed as the new baseline instead of POSTed, dropping the final change. Persist the serialization baseline on the entry (mirrorBaseline) and, on a re-home, seed the mirror from it without re-arming the skip — so a genuine unobserved change still syncs while an unchanged inherited value still doesn't POST. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): make draft autosave mirror component-independent Replaces the re-home approach (Codex review): re-homing the mirror to the last acquirer assumed LIFO holder lifetimes, which the sessions UI breaks — it keeps multiple warm session previews mounted at once, so two warm previews of one draft share the entry and closing the newer one killed autosave in the surviving older one. Instead create the entry's mirror $effect.root in a microtask, where no component/effect is active, so it is a true top-level root owned by the ENTRY: it survives every holder unmounting and is disposed only at refcount 0. Removes the re-home/baseline bookkeeping entirely. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(frontend): condense mirror-deferral comment per review Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1e192f2d86 |
feat(apps): authorize deployed-app S3 reads on-behalf of the author for logged-in viewers (#10048)
* feat(apps): authorize deployed-app S3 reads on-behalf of the author for logged-in viewers A logged-in user viewing a deployed app now reads S3 files (rich result, table/image/PDF preview, CSV export, download, metadata) the same way an anonymous viewer already does: on-behalf of the app author per the app policy's execution_mode, gated by an app-provenance check — instead of against the viewer's own S3 permissions. This aligns S3 with every other thing an app does (scripts, flows, resources all already run on-behalf of the author) and lets an operator who lacks folder S3 permission still see data rendered inside the app. The raw job_helpers/* S3 API stays viewer-scoped: a viewer who lacks folder permission is still denied there. Only which endpoint the app frontend uses for logged-in deployed viewers changes. Backend: - Add app-scoped, provenance-gated apps_u/* variants for all S3 display ops (download_s3_file already existed; add download_s3_parquet_file_as_csv, load_file_metadata, load_file_preview, load_parquet_preview, load_csv_preview, load_table_count). Each routes through one shared helper (app_s3_on_behalf_and_provenance) that scope-confines an app embed token, resolves the on-behalf identity, and runs the provenance gate ONCE before dispatching to the EE *_internal S3 helpers. - Close the confused-deputy hole in check_if_allowed_to_access_s3_file_from_app: the unconditional Ok() bypass for a logged-in, non-embed session now only applies in viewer execution mode (where the on-behalf identity IS the viewer, so the viewer's own permissions still bound the read downstream). Author-mode reads (anonymous/publisher) always enforce provenance, for anonymous and logged-in viewers alike, so a viewer cannot launder the author's S3 permissions with an arbitrary file_key. Frontend: - Route the deployed-app view through apps_u/* using the app-viewer isEditor signal instead of login state (the old $userStore proxy wrongly sent logged-in deployed viewers to the viewer-scoped job_helpers API). Editor and preview keep viewer identity via job_helpers. execution_mode: viewer remains the escape hatch for per-viewer S3 enforcement. Fixes provenance-gated S3 display for logged-in operators on deployed apps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(backend): document cargo features, restarting the dev backend, and filesystem object storage The dev backend runs `cargo watch --features quickjs` by default, which omits S3, EE, MCP, and non-JS runtimes — feature-gated routes then 404 or return a "requires <feature>" stub at runtime. Add a backend/CLAUDE.md section that: - explains that you must restart the backend with the appropriate features to exercise gated functionality, with the pid/cwd-scoped restart recipe (never pkill target/debug/windmill) and the PORT=$BACKEND_PORT gotcha; - documents what each commonly-toggled feature gate does (private, enterprise, license, parquet, duckdb, language runtimes, mcp, trigger kinds, no_auth) plus common combinations; - documents using the built-in FilesystemStorage large-file storage for dev workspace object storage (hidden from the UI dropdown; set via edit_large_file_storage_config), including the advanced_permissions shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): don't flatten inner query in app-scoped S3 preview routes axum's `Query` uses `serde_urlencoded`, which cannot deserialize the typed (numeric/bool) fields of a `#[serde(flatten)]`-ed struct and 400s on `limit` / `offset` ("invalid type: string, expected u32"). The app-scoped load_csv_preview / load_parquet_preview / load_table_count routes flattened LoadPreviewQuery / LoadCountQuery, so their previews were broken. Restate the fields directly on the outer query structs (with an into_inner() to rebuild the inner query) and extend the CE OSS stub to match. Also bumps ee-repo-ref.txt for the companion EE csv-separator panic fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address CI review — nested DisplayResult routing, byte-range contract, docs, tests - [P1] Thread `appPath` into the nested `DisplayResult`s (render_all children and the expanded-result drawer) so logged-in deployed viewers route nested/expanded S3 tables, images, PDFs, and downloads through `apps_u/*` too, not job_helpers. - [P2] Mark `read_bytes_from`/`read_bytes_length` required on the `apps_u/load_file_preview` route (they are non-optional in LoadFilePreviewQuery), and mirror the full query shape in the CE OSS stub so the byte-range contract is enforced identically on CE and EE. - [P2] Fix the backend retrigger command in backend/CLAUDE.md: cargo watch runs from `backend/`, so `touch README.md` (not `backend/README.md`). - [P2] Trim app_s3_onbehalf.rs comments per AGENTS.md (state the invariant once, no drafting-history narration). - Extend the integration test to cover the table-count, csv-preview (numeric limit/offset deserialization), and file-preview (byte-range required) routes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(apps): tighten S3 provenance-gate comments per AGENTS.md Consolidate the viewer-mode / author-mode rationale to ≤4 lines at each branch of the gate, and drop the repeated explanation from the shared app_s3_on_behalf_and_provenance doc comment (which now just states what the helper does). No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to f292a1040da6a667ce7c22abf63ec0debfdd480f This commit updates the EE repository reference after PR #657 was merged in windmill-ee-private. Previous ee-repo-ref: a582389084eb363997cb5e8053f29220e0d3eaec New ee-repo-ref: f292a1040da6a667ce7c22abf63ec0debfdd480f 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> |