Commit Graph
8530 Commits
Author SHA1 Message Date
Guilhem LemouelandClaude Opus 4.8 2fdafdddaf feat(ai): check raw app frontend compilation from the chat
get_lint_errors on a raw app without a runnable key now compiles the app's
frontend files and reports where the build broke, so editing app files can
be checked the same way editing a runnable already was. A frontend is a
bundle rather than a set of independent documents, so there is no language
service to query per file — only the compiler's verdict, which also means
files unreachable from the entry point are not covered.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 14:26:30 +02:00
Guilhem LemouelandClaude Opus 4.8 aaa7601950 fix(ai): correct headless lint ownership, staleness and timeouts
Review findings on the headless linter:

- An editor can adopt a model headless linting created, so attachment now
  decides who owns a buffer rather than who created it. The editor likewise
  applies its own content when adopting an existing model, instead of
  displaying whatever was last linted.
- Markers are cleared when an owned model is rewritten: they outlive the
  content they describe, and a matching count let the settle loop return
  the previous version's diagnostics.
- Eviction no longer drops the URI it is keeping, which had left a live
  model untracked and subsequently treated as belonging to an editor.
- A language server that stalls is named in the result rather than
  contributing an empty list, and no longer discards what other servers
  already reported.
- A client abandoned at its deadline is stopped, so it cannot keep holding
  the global command names every later lint of that language needs.
- Deno's import map is built from the path its document actually has.
- Pre-lint network calls are bounded; lints of one URI queue behind each
  other, so a hung request would otherwise wedge that item indefinitely.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 14:18:45 +02:00
Guilhem LemouelandClaude Opus 4.8 0236c3b145 feat(ai): headless linting for python, go, deno and bash
Extends get_lint_errors beyond the TypeScript worker to the language-server
languages. These servers are driven directly rather than through the editor's
clients: pyright and gopls implement LSP 3.17 pull diagnostics and only get
asked for documents visible in an editor, so headless the request has to be
made explicitly. Servers that answer with nothing are still given a bounded
window to publish instead, which is how ruff reports.

A server whose client cannot start — its global command names are taken when
an editor is already running one — is named in the result instead of silently
shrinking it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 13:29:17 +02:00
Guilhem LemouelandClaude Opus 4.8 f5b9dc7f5c refactor(editor): extract language server configuration
Diagnostics for python, go, deno and shell are decided by which server runs
and with which initialization options, so headless linting has to build its
clients from the same descriptors. Move them to lint/lspLanguageConfig.ts;
the editor keeps owning the websocket lifecycle and reconnection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:59:37 +02:00
Guilhem LemouelandClaude Opus 4.8 11b58cacb5 feat(ai): get_lint_errors tool for global chat
Global chat edits drafts with no editor mounted, so until now it had no way
to type-check what it wrote. The tool resolves draft or deployed code for a
script, an inline flow module, or a raw app backend runnable, lints it
headlessly at the URI the editor would use, and reports unsupported
languages explicitly rather than a misleading clean result.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:48:50 +02:00
Guilhem LemouelandClaude Opus 4.8 7147261450 feat(ai): headless TypeScript lint service
Lints script, flow-step and raw-app runnable code with no editor mounted, by
driving the same Monaco model, extra libs and acquired types an editor would.
Settling waits for the worker's own diagnostic count rather than a fixed
sleep, which also terminates immediately on clean code, where no marker
change event is ever emitted. Editor.getLintErrors now reads markers through
the same helper, so the two cannot drift apart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:43:39 +02:00
Guilhem LemouelandClaude Opus 4.8 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 LemouelandClaude Opus 4.8 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 LemouelandClaude Opus 4.8 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
hugocasaandClaude Opus 4.8 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
hugocasaandClaude Opus 4.8 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
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
GuilhemandClaude Fable 5 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
GuilhemandClaude Fable 5 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
hugocasaandClaude Fable 5 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
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 Fiszelandrubenfiszel 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>
2026-07-15 21:29:39 +02:00
Ruben FiszelandClaude Opus 4.8 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 Fiszelandrubenfiszel 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>
2026-07-15 18:00:53 +02:00
GuilhemandClaude Opus 4.8 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
GuilhemandClaude Opus 4.8 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
hugocasaandClaude Fable 5 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 Fiszelandrubenfiszel 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>
2026-07-15 10:02:01 +02:00
hugocasaandClaude Opus 4.8 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 FiszelandClaude Opus 4.8 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 Fiszelandrubenfiszel 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>
2026-07-14 22:38:45 +02:00
GuilhemandClaude Opus 4.8 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
hugocasaandClaude Opus 4.8 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
GuilhemandClaude Opus 4.8 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
GuilhemandClaude Opus 4.8 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>
2026-07-14 19:02:22 +02:00
hugocasaandClaude Opus 4.8 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>
2026-07-14 17:19:42 +02:00
GuilhemandClaude Opus 4.8 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>
2026-07-14 17:18:01 +02:00
GuilhemandClaude Opus 4.8 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>
2026-07-14 15:52:20 +02:00
Alexander PetricandClaude Opus 4.8 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>
2026-07-14 15:17:26 +02:00
Ruben FiszelandClaude Opus 4.8 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>
2026-07-14 15:15:48 +02:00
GuilhemandClaude Opus 4.8 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>
2026-07-14 13:21:06 +02:00
Ruben Fiszelandrubenfiszel 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>
2026-07-14 12:47:53 +02:00
hugocasaandClaude Opus 4.8 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>
2026-07-14 12:12:00 +02:00
Ruben Fiszelandrubenfiszel 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>
2026-07-14 10:46:55 +02:00
Ruben FiszelandClaude Opus 4.8 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>
2026-07-14 10:29:48 +02:00
Ruben Fiszelandrubenfiszel 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>
2026-07-12 10:30:58 +02:00
Ruben FiszelandClaude Opus 4.8 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>
2026-07-12 10:19:08 +02:00
Ruben Fiszelandrubenfiszel 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>
2026-07-12 00:01:39 +02:00
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>
2026-07-11 23:46:34 +02:00
Ruben FiszelandClaude Opus 4.8 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>
2026-07-11 16:00:53 +02:00
Ruben FiszelandClaude Opus 4.8 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>
2026-07-11 15:50:41 +02:00
Ruben FiszelandClaude Opus 4.8 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>
2026-07-11 11:54:08 +02:00