Commit Graph

13935 Commits

Author SHA1 Message Date
Guilhem Lemouel e47d760eba refactor(editor): extract ATA setup and lsp token root
Headless linting needs the same acquired types as the editor, npm
declarations and relative-import models alike. Move the ATA delegate and
token root to lint/typescriptAta.ts; the editor keeps its trigger logic and
supplies the model-revalidation nudge via onLocalFileRegistered.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:38:04 +02:00
Guilhem Lemouel 35f3196c4a refactor(editor): extract typescript extra-libs registration
Headless linting must register the same rt.d.ts resource-type namespace and
custom windmill-client declarations as a mounted editor, since diagnostics
depend on them. Move the apply bodies to lint/typescriptExtraLibs.ts; the
editor keeps its reactive resource() wrapper around the fetch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:35:06 +02:00
Guilhem Lemouel b89e082e87 refactor(editor): extract Monaco model path/URI computation
Headless linting needs to derive the exact model URI the editor uses;
computeUri/computePath were component-local closures over scriptLang and
lang. Move them to lint/monacoUri.ts with those as explicit parameters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:30:13 +02:00
hugocasa 7b813d1f74 fix(frontend): sanitize job result markup, gate it on unsandboxed public apps (#10127)
* fix(frontend): sanitize html and svg result rendering

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(frontend): add dompurify to lockfile root deps

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): keep sanitizing rich results on public app surfaces

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): gate risky app markup on unsandboxed public surfaces

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): derive app markup isolation from the real origin

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* style(frontend): use the design-system danger alert for the markup gate

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 15:49:20 +02:00
hugocasa 24750e6ef1 fix(raw-apps): prevent and surface the silent blank screen from an unmounted #root (#10150)
* fix(raw-apps): prevent and surface the silent blank screen from an unmounted #root

An `index.tsx` written as a bare `export default function App() {...}` with
no mount call builds and runs without throwing: the preview executes the
bundle against an empty `<div id="root">` and auto-renders nothing, so the
JSX never runs, nothing reaches the console or the runtime-error overlay,
and the app is blank with no diagnostic.

Prevent it: the raw-app system prompt and the in-chat app prompt now state
that `index.tsx` is the mount entrypoint, show the mount shim for React,
Svelte and Vue, and call out that a bare component fails silently.

Surface it: when a build still mounts nothing, the preview harness posts
`emptyRender` and the editor shows an error overlay naming the missing
call. The harness reports only when nothing is on screen AND the app never
looked `#root` up, so an app that mounted but paints nothing yet (a fetch
in flight, an unresolved Suspense) is never flagged; `renderAppeared`
withdraws the overlay if a deferred mount lands late.

The handlers stay dormant until the builder tarball that emits these
messages is pinned via `ui_builder_artifact.json`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(raw-apps): pin the UI builder artifact that emits emptyRender

Activates the "Nothing was mounted" overlay: the pinned tarball predates
the harness change, so the host handlers were dormant until now.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: drop a screenshot accidentally committed at the repo root

Not referenced anywhere; the PR's screenshots are hosted externally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(raw-apps): make the unmounted-app guidance framework-aware

The detector fires for every raw-app framework, but the overlay and the
prompts named React's `index.tsx` and `createRoot` unconditionally. Svelte
and Vue apps mount from `index.ts` via `mount` / `createApp`, so the
guidance pointed at a nonexistent entrypoint and an unavailable API.

Derive the entrypoint and mount call from the app's files, keyed off file
extensions rather than the template filenames, which users rename.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 15:48:50 +02:00
Guilhem fa03984a14 fix(ai): show the question in askUserQuestion tool-call labels (#10153) 2026-07-16 15:48:24 +02:00
Diego Imbert 7d5009e392 fix: heartbeat job ping during s3object materialization in SQL executors (#10152)
* fix: heartbeat job ping during s3object materialization in SQL executors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KrFtZjXc8GB6VtSMVJFXjE

* chore: update ee-repo-ref.txt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KrFtZjXc8GB6VtSMVJFXjE

* chore: update ee-repo-ref to e19948fa2974a7d89bec12957fc6d9fa0a421da8

This commit updates the EE repository reference after PR #668 was merged in windmill-ee-private.

Previous ee-repo-ref: a3828dcd67f026c0e983a1a5dc5c5b33af3c3120

New ee-repo-ref: e19948fa2974a7d89bec12957fc6d9fa0a421da8

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-07-16 15:48:11 +02:00
Ruben Fiszel 3bd9f05938 fix(alerts): identify server replica in low-disk alert + per-host dedup tag (#10143)
* fix(alerts): identify server replica in low-disk alert + per-host dedup tag

The server-mode low-disk alert keyed its dedup tag on the mountpoint alone,
so `simple_alert_helper` mapped every server replica onto a single alert row
per mountpoint. With more than one replica that row flaps every monitor pass:
a replica seeing low disk raises the alert while a replica seeing healthy disk
recovers it. The alert text also could not say which replica tripped.

The fix lives in windmill-ee-private (`low_disk_alerts` in
windmill-common/src/ee.rs) and appends the hostname to both the message and
the dedup tag, mirroring the worker branch.

Also add a regression test pinning the server tag as per-host, and correct the
monitor cadence comments: iterations are LISTEN_NEW_EVENTS_INTERVAL_SEC
(10s by default), not 30s, so "~60s (2 iterations * 30s)" was wrong on both
factors.

* fix(alerts): widen healthchecks.check_type so per-host disk tags fit

Alert tags embed a mountpoint and a hostname, both unbounded, but check_type
was varchar(50). create_alert only logs the insert error while the
notification still fires, so an overflowing tag re-alerts every monitor pass
and never records recovery state.

The server tag overflows for ordinary pod-length hostnames, and the existing
worker tag already overflows for every tracked mount except "/". Widening the
column fixes both; bounding the hostname would not, since the mountpoint alone
can consume the budget.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to b3d01f2c0d2c0714ae95b8a348af22b0fcc30ee4

This commit updates the EE repository reference after PR #666 was merged in windmill-ee-private.

Previous ee-repo-ref: ccd1e42cf6b2d051ca17074fbdf5b80a46cffe0f

New ee-repo-ref: b3d01f2c0d2c0714ae95b8a348af22b0fcc30ee4

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>
2026-07-16 15:21:54 +02:00
Ruben Fiszel 4e0fd4db55 feat(alerts): include disk total and top consumers in low-disk alert (#10144)
* feat(alerts): include disk total and top consumers in low-disk alert

Point ee-repo-ref at the companion windmill-ee-private commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(alerts): pass alert message lazily from the min-version check

simple_alert_helper now takes the error message as a future so callers can
put diagnostic work behind it. Update this call site and point ee-repo-ref
at the companion windmill-ee-private commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: point ee-repo-ref at rebased EE branch

Rebase onto EE main so the pin keeps the SAML metadata fixes (394ad23)
that the previous ref carried, and pick up the mount-scoped consumer walk.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: bump ee-repo-ref for bind-mounted file exclusion

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to 4d7aafdba33a879b3c60d390c960e57679da9e87

This commit updates the EE repository reference after PR #667 was merged in windmill-ee-private.

Previous ee-repo-ref: 08d3aa4c5bf630d15a28289cca62a0f1da7b7386

New ee-repo-ref: 4d7aafdba33a879b3c60d390c960e57679da9e87

Automated by sync-ee-ref workflow.

* chore: point ee-repo-ref at the merged EE work plus the test fix

ee#667 squash-merged, so the previous pin was a branch commit no longer
reachable from EE main. Point at ee#669, which branches from EE main and
carries the /proc test-portability fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to d8a7ac6ae97642a7f4928e6be6846a32dabf4e26

This commit updates the EE repository reference after PR #669 was merged in windmill-ee-private.

Previous ee-repo-ref: 5526aedd73654b9aa4086dae0441b9687ff6415d

New ee-repo-ref: d8a7ac6ae97642a7f4928e6be6846a32dabf4e26

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>
2026-07-16 14:58:26 +02:00
Guilhem 568dbbee85 fix(frontend): show friendly draft path for draft-only items in pickers (#10136)
* fix(frontend): show friendly draft path for draft-only items in pickers

* fix(frontend): dedupe current draft item and scope tab picker by friendly path

* fix(frontend): key live draft picker entries by storage path

* fix(frontend): fall back to the current leaf when the picker highlight key vanishes

* fix(frontend): remount session tab picker when the friendly scope arrives

* fix(frontend): stamp staged tab path for deployed items with undeployed renames

* fix(frontend): expose staged flow/raw-app renames through the live draft registration
2026-07-16 14:56:57 +02:00
Guilhem 7fda6a0534 feat(frontend): flatten workspace pickers, whole-tab picker trigger (#10145)
* fix(frontend): open session preview picker from whole tab, anchor to tab edge

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(frontend): flatten session preview picker to workspace home level

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(frontend): flatten chat context picker workspace tree

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): show root loading state in flat drill pickers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): make tab-strip keyboard activation work inside dnd zones

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): return focus to tab after active-tab picker closes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): move focus with selection on arrow-key tab navigation

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:36:45 +02:00
Guilhem cc305b1d97 refactor(frontend): reorder sidebar settings menu, move logout to user submenu (#10149)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:36:01 +02:00
hugocasa 4ee1d32101 feat: display openai reasoning summaries in ai chat (#10147)
* feat(frontend): display openai reasoning summaries in ai chat with unverified-org fallback

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): scope hidden-thinking hint per workspace/provider and skip summary on explicit reasoning-off

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): compose responses fallbacks in either error order and track all unavailable summary keys

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:31:10 +02:00
hugocasa 0694b84da7 fix(frontend): surface real tool call errors in AI chat (#10146) 2026-07-16 11:19:19 +02:00
AlexRV12 0ea570570e feat(ai-sessions): CRUD markdown artifacts in sessions (#10046)
* feat: add IndexedDB persistence layer for AI-chat artifacts

* feat: add reactive store for AI-chat artifacts

* feat: add artifact chat tools and wire store lifecycle

* feat: add markdown artifact viewer with source toggle

* feat: surface session artifacts in the preview panel and chat list

* feat: tell the copilot when to use artifacts in the session prompt

* test(ai_evals): add artifact case and wire artifact helpers for session context

* fix(copilot): keep in-memory artifacts across same-session resyncs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: unify session composer edits/artifacts/jobs into a status line

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: add an artifacts section to the session preview picker

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: share markdown prose presets and restyle the artifact viewer

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor: unify session status popovers into one keyboard-navigable shell

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: reset first-block top margin in all markdown prose presets

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: open the preview picker on the artifacts branch for an active artifact

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: keep artifact picker scope independent of branch hydration state

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Guilhem Lemouel <guilhemlemouel@gmail.com>
2026-07-16 11:17:20 +02:00
Ruben Fiszel a935d06c8e chore(main): release 1.760.1 (#10142)
* chore(main): release 1.760.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.760.1
2026-07-15 21:29:39 +02:00
Ruben Fiszel 8c725d9e44 fix(apps): honor presigned S3 signature on app display/preview routes (#10141)
The app provenance gate short-circuits on a valid presigned signature, but only the raw download_s3_file route parsed it. The parquet/csv/table-count/file-preview/metadata routes discarded sig/exp and always fell through to the provenance gate, so a presigned S3 object rendered as a table showed "File restricted" for any viewer who did not produce it. Thread sig/exp through every apps_u S3 display route and forward the presigned bearer from ParqetCsvTableRenderer/DisplayResult.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:18:15 +02:00
Ruben Fiszel 2092155191 chore(main): release 1.760.0 (#10128)
* chore(main): release 1.760.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.760.0
2026-07-15 18:00:53 +02:00
Guilhem 9705d60284 fix(frontend): keep session-exit URL clean by syncing new_draft strip with the router (#10101)
* fix(frontend): keep session-exit URL clean by syncing new_draft strip with the router

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(frontend): correct replaceState comment and test-mock wording per review

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(frontend): correct replaceState comment and drop drafting-history phrasing

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:56:19 +02:00
Guilhem af177cefe0 fix(frontend): graceful small-screen timeframe picker on the runs page (#10073)
* fix(frontend): prevent runs timeframe calendar popover overflow on small screens

The Runs page timeframe picker rendered its popover as a wide 3-column row
(preset list + two side-by-side calendars). With the right-aligned trigger and
a center-anchored `bottom` placement, the popup ran off the right edge on
narrow viewports.

Anchor the popover to the right edge (`placement="bottom-end"`) and make its
content reflow to a vertical stack below the `sm` breakpoint, capped at
`max-w-[calc(100vw-2rem)] max-h-[80vh] overflow-auto` so it can never exceed the
viewport. The desktop side-by-side layout is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): compact runs timeframe picker with a Start/End toggle on small screens

The two-calendar desktop popover needs ~780px (two min-w-9 grids + presets +
popover padding); below that it overflows. Under 800px, show a single calendar
with a Start/End toggle picking which bound it edits, using set-start/set-end so
each bound keeps its date and HH:MM time inputs — the same precision the desktop
start/end pair offers.

On short/landscape viewports the compact panel is scroll-contained within the
popover's fitViewport height (contentClasses overflow-y-auto, scoped to the
small layout) so its lower controls stay reachable. The desktop two-calendar
layout is unchanged.

Presets are shared between both layouts via a snippet, and the active range is
preserved across the breakpoint since both branches drive the same value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): let InlineCalendarInput month/year selects portal, use in compact timeframe picker

Add an opt-in `portalSelects` prop to InlineCalendarInput that portals the
month/year dropdowns to the body (default keeps them in-flow, so existing
consumers are unchanged). The compact runs timeframe picker enables it so the
dropdowns escape its scroll-contained (overflow-y-auto) popover instead of
being clipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:55:21 +02:00
Diego Imbert 2fe999f66c fix(frontend): treat a displaced draft save as superseded, not failed (#10094) 2026-07-15 17:55:01 +02:00
hugocasa 8bfe5c9340 fix(ai): stop sending the AI agent system prompt twice for OpenAI (#10126)
* fix(ai): stop sending the AI agent system prompt twice for OpenAI

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai): document collect_system_prompt precedence and trim duplicate comments

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai): hoist only the leading system prompt for OpenAI

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:53:09 +02:00
Ruben Fiszel 17872018cc feat(nsjail): make python/ansible rlimit_as configurable per worker (GIT-921) (#10138)
nsjail caps a jailed job's virtual address space at rlimit_as (4096 MiB for
python3 and ansible). JIT runtimes (Bun/JavaScriptCore, the JVM) reserve large
virtual ranges up front, so a subprocess spawned from a jailed Python/Ansible
job can crash against this cap even when its physical memory use is modest
(e.g. the Bun-compiled claude CLI hitting JSC/pthread allocation failures).

Most other language protos already run with disable_rl: true (unlimited);
python3 and ansible are the outliers with an explicit rlimit_as. This exposes
that cap via a per-language env var (NSJAIL_PY_RLIMIT_AS_MB,
NSJAIL_ANSIBLE_RLIMIT_AS_MB) so operators can raise or lift it on a dedicated
worker pool without a source patch/rebuild and without weakening the
mount/PID/user-namespace isolation that provides the real security boundary.
Only the address-space limit changes; cpu/fsize/nofile rlimits are untouched.

Value is in MiB, or unlimited/none/inf/0 to uncap (rlimit_as_type: INF). Unset
keeps the historical 4096 default.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:52:39 +02:00
Ruben Fiszel f7eb5c460d fix(apps): invalidate cached app policy on change or deletion (GHSA-r5v4-cxh9-7qhq) (#10121)
* fix(apps): invalidate cached app policy on change or deletion (GHSA-r5v4-cxh9-7qhq)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(agents): keep PR tests and comments minimal and non-ephemeral

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:41:54 +02:00
hugocasa bd3adc9781 fix(frontend): only carry custom-tag overrides on 'Run again' (#10137)
* fix(frontend): only carry custom-tag overrides on 'Run again'

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): keep literal overrides on dynamic-tag reruns

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:33:42 +02:00
Ruben Fiszel 5626768471 fix(cli-image): patch fixable CRITICAL CVEs in windmill-cli image (GIT-922) (#10135)
The published ghcr.io/windmill-labs/windmill-cli image shipped two fixable
CRITICAL findings:

- openssl (libssl3t64, openssl-provider-legacy): stale in the oven/bun:slim
  base image (CVE-2026-34182). Fixed by running apt-get upgrade so the image
  picks up the patched Debian packages.
- vitest 2.1.9 (CVE-2026-47429 / GHSA-5xrq-8626-4rwp): a dev-only
  devDependency reference in esrap's cached package.json living in bun's
  package download cache. The cache is unused at runtime, so it is removed
  after install.

Validated by building the image and scanning with Trivy: openssl now reports
3.5.6-1~deb13u2 (fixed) and vitest is entirely absent. wmill still runs. The
only remaining CRITICALs are perl-base CVEs with no upstream fix available.

Fixes GIT-922

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 15:21:16 +02:00
Ruben Fiszel 188647a942 fix(security): enforce variables:write scope on resource-delete var cascade (GHSA-xmr2-98m6-cjf7) (#10123)
A token scoped only to resources:write:<path> could delete linked secret
variables it had no variables:write scope for, by embedding $var:<victim>
in an attacker-controlled resource value and triggering the resource-delete
cascade. #9712 re-enforced scoped-token boundaries broadly but missed this path.

Add check_linked_var_delete_scopes, called before the cascade in both
delete_resource and delete_resources_bulk: require variables:write for every
linked variable, failing (and rolling back) the delete otherwise. No-op for
unscoped tokens, so full-token cascade cleanup is unchanged.

No co-located-path exemption: a resource and a variable may share a path, and a
resource-write token can create a resource over an existing standalone variable
and self-reference it, so "same path as the deleted resource" is attacker-
forgeable and cannot stand in for variable scope.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:19:17 +02:00
Ruben Fiszel 6407d9ff5c fix(bash): normalize CRLF line endings before running scripts (#10131)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:46:26 +02:00
Ruben Fiszel 27ead8d084 fix(ai): disable redirects on worker AI provider client (GHSA-5q4v) (#10122)
* fix(ai): disable redirects on worker AI provider client (GHSA-5q4v)

The worker AI request path issued provider requests with the shared
HTTP_CLIENT, which follows up to 10 redirects without revalidating each
hop. SSRF validation on the provider base_url is single-shot, so a public
base_url could 3xx the worker into a private/internal host (e.g. cloud
metadata), bypassing the private-endpoint protection. The API proxy was
already hardened in #9370; the worker path was missed.

Add a dedicated AI_HTTP_CLIENT with redirects disabled and use it for the
user-controlled provider endpoint, mirroring the API proxy client.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai): add ALLOW_AI_BASE_URL_REDIRECTS debug escape hatch

Off by default (redirects stay disabled). When set, restores redirect
following on the AI HTTP client for debugging non-standard/self-hosted
gateways, with a startup warning that it weakens SSRF protection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(ai): correct redirect comment for the escape hatch override

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(ai): condense redirect invariant comments per review

Anchor the SSRF rationale to ALLOW_AI_BASE_URL_REDIRECTS (the knob that
would break it) and shorten the AI_HTTP_CLIENT and call-site comments to
avoid restating it at multiple sites (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>
2026-07-15 11:24:26 +02:00
Ruben Fiszel 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>
2026-07-15 11:07:04 +02:00
Ruben Fiszel 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
2026-07-15 10:02:01 +02:00
hugocasa 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>
2026-07-15 10:01:35 +02:00
Ruben Fiszel 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>
2026-07-15 10:00:50 +02:00
Kobi Hikri 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.
2026-07-15 09:52:50 +02:00
Ruben Fiszel eb7a2e048b ci: cap build jobs and disable incremental in backend tests to prevent OOM (#10118) 2026-07-15 09:50:16 +02:00
Ruben Fiszel 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>
2026-07-15 09:47:54 +02:00
Ruben Fiszel 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>
2026-07-15 09:35:55 +02:00
Ruben Fiszel 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>
2026-07-15 09:04:50 +02:00
Ruben Fiszel 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>
2026-07-15 08:56:05 +02:00
Ruben Fiszel 88030d0f55 fix(tree-view): align file indentation with sibling folders (#10115)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 02:03:47 +02:00
Ruben Fiszel 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>
2026-07-15 01:58:56 +02:00
Ruben Fiszel 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>
2026-07-15 01:56:41 +02:00
Ruben Fiszel 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>
2026-07-15 01:53:43 +02:00
Ruben Fiszel 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>
2026-07-15 01:50:50 +02:00
Ruben Fiszel 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>
2026-07-15 01:49:48 +02:00
Ruben Fiszel 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>
2026-07-15 01:08:55 +02:00
Ruben Fiszel 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
2026-07-14 22:38:45 +02:00
Guilhem 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>
2026-07-14 22:33:55 +02:00
hugocasa 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>
2026-07-14 22:33:16 +02:00
Guilhem 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>
2026-07-14 22:29:34 +02:00