mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
3aaceb7efb3bd232da73b79fcdcf4c712caad7d3
14045 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3aaceb7efb |
fix(ci): make /review idempotent per head commit, re-run cancelled reviews in place (#10283)
* fix(ci): make /review idempotent per head commit, re-run cancelled reviews in place `/review` fanned out to codex/pi/claude unconditionally. A push already auto-triggers codex/pi (and claude on open) against the PR head, so the comment-driven relaunch both cancelled those in-flight auto runs (shared concurrency group) and landed its own status on main — issue_comment runs never attach a check to the PR head — leaving the PR showing only a cancelled review that never resolves. Add a `plan` job that, for the `/review` fan-out, decides per agent: skip when a running or successful review already covers the head commit; re-run the head's cancelled/failed run in place (a re-run keeps the original pull_request event so its checks re-attach to the PR head); launch a fresh run when nothing usable covers the head commit (no runs, or only a skipped draft/fork-gated run). Explicit /codex, /pi, /claude remain deliberate re-reviews and always launch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ci): make explicit /codex idempotent per head, track fresh launches on head SHA Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
14c29b77e9 |
fix(cli): surface shared UI changes in sync push dry-run preview (#10278)
* fix(cli): surface shared UI (ui/) changes in sync push dry-run preview The git-sync "Pull from repo" preview never showed shared UI (ui/) changes, so users thought the shared-UI folder was not syncing. The apply step does sync it (pushSharedUi on dryRun=false); only the dry-run preview was blind. Shared UI maps a single top-level ui/ folder to the workspace_shared_ui store and is handled out-of-band from the normal file diff (isNotWmillFile excludes ui/). The dry-run path returns before pushSharedUi runs, so the `changes` list the modal consumes never contained any ui/ entry and read as "no changes". - Add exported diffSharedUi(workspace) computing added/edited/deleted ui/<rel> entries (push direction), and refactor pushSharedUi to reuse it so preview and apply never diverge. - Fold the diff into `changes` in the dry-run path (both JSON and terminal), guarded by try/catch. Apply path is unchanged. - Label ui/ paths as "shared UI" in prettyChanges (getTypeStrFromPath throws on non-wmill paths like ui/config.json). - Do not run pushSharedUi in the zero-changes branch during a dry-run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): report shared-UI-only push in sync JSON output Address local review: when a real apply has only ui/ changes it reaches the zero-file-changes branch, pushes the shared-UI store, then printed "No changes to push" in --json-output. Surface pushSharedUi's result so the message no longer claims no changes when the store was written. Also correct the pushSharedUi docstring (empty-but-existing folder still clears a non-empty remote store). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(cli): trim shared_ui diff test header to the durable invariant Address Codex nit: replace the narrative regression header with a 4-line statement of the invariant (diffSharedUi mirrors pushSharedUi's apply semantics so preview and apply never diverge). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): own-property shared-UI diff and count ui/ in dry-run summary Address Codex review: - diffSharedUi used `rel in remote`/`rel in files`, so a file named after an Object.prototype member (e.g. ui/toString) always registered as present and was misdiffed; pushSharedUi could then skip deleting it. Use Object.hasOwn. - The dry-run "N changes to apply" summary logged before the shared UI fold, so a shared-UI-only dry-run printed "0 changes to apply" then listed the changes. Fold before the summary so the count includes ui/. - Add a unit test for the ui/toString inherited-property filename. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0f1b8641f2 |
feat(windows): enable ruby and rlang on the windows worker (#10279)
Adds ruby and rlang to all_languages_windows so the Windows worker carries the same language set as Linux (all_languages). The only remaining difference is mssql-winauth vs mssql-kerberos, which is the correct per-OS integrated-auth backend. Both are parser-only Rust crates with no dependencies, so the build/binary cost is negligible; the ruby and r executors already have #[cfg(windows)] branches. When the Ruby/R runtime is absent on the host, a job degrades to a runtime "command not found", same as any other missing runtime. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
05157861aa |
chore(frontend): bump ui_builder to 1f1fe4f (raw-app deploy minify + no source map) (#10280)
Picks up windmill-code-ui-builder #22 + #23: the raw-app deploy bundler (RawAppBundlerHost) now emits a minified bundle with no source map instead of the editor-preview defaults (unminified + inline base64 map). Deployed raw apps were being served multi-megabyte bundles by apps_u/get_data, which also amplified S3 fetch latency into slow app loads. The in-editor preview is unchanged. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c50a2abad0 |
fix(jobs): sanitize NUL in completed job result before jsonb insert (#10274)
## Summary
A job whose result contains a real NUL (U+0000) serializes to a `\u0000` JSON escape that the `jsonb`-typed `v2_job_completed.result` column rejects with Postgres `22P05` ("unsupported Unicode escape sequence"). This aborts the `INSERT` in `commit_completed_job`, which then retries 10 times and leaves the job unable to complete (surfaced as `Could not add completed job <id>: ... unsupported Unicode escape sequence`).
The fix sanitizes the serialized result immediately before the insert, with effectively zero overhead on the common NUL-free path.
## Changes
- **Promote `strip_json_nul` into `windmill-common`** (`utils.rs`): `fn strip_json_nul(&str) -> Cow<str>` — a `contains("\\u0000")` fast guard returns the input borrowed when clean; only a genuine odd-parity NUL escape triggers the O(n) rebuild. `Cow::Owned` is returned **only** when a NUL was actually stripped, so a legitimate `\\u0000` (escaped backslash + literal text) borrows through untouched. Replaces the two duplicated copies previously in `windmill-api/src/drafts.rs` (`strip_json_nul`) and `windmill-api/src/apps.rs` (`strip_null_chars`); both call sites now use the shared helper.
- **Add `serialized_json()` to the `ValidableJson` trait** (`windmill-queue/src/jobs.rs`): `Box<RawValue>` returns `Cow::Borrowed(self.get())` (zero-cost, already serialized); other impls serialize on demand via `to_raw_value`.
- **`commit_completed_job`** binds `strip_json_nul(result.serialized_json())` as `$3::text::jsonb` in both the `INSERT ... SELECT` and the `ON CONFLICT ... result = $3` (was `result as Json<&T>`). Stored data is unchanged (Postgres parses JSON text into `jsonb` identically); `wm_labels`/`result_metadata` still operate on the typed `T`.
- **Regenerated the sqlx offline cache** (one query file swapped; EE caches preserved).
- **Doc:** updated the stale `strip_null_chars` reference in `windmill-api-workspaces/src/workspaces.rs` to point at the shared `strip_json_nul`.
## Test plan
- [x] `cargo check -p windmill-queue -p windmill-api -p windmill-common -p windmill-api-workspaces` — clean, no warnings
- [x] `strip_json_nul` unit tests in `windmill-common` (clean-borrow, real-NUL, legit-escape borrow no-op, collision, nested keys/values, odd-run): 6 passed
- [x] End-to-end regression in `backend/tests/nativets_jobs.rs` (`--features deno_core`): a JS job returning a genuine NUL and a literal `\\u0000` completes, storing `"ab"` (stripped) and `"a\\u0000b"` (preserved). Without the fix the insert aborts and the job never completes.
- [x] `backend/tests/drafts_nul.rs` integration test still passes (helper refactor intact)
|
||
|
|
d7a0078b58 |
fix(resources): apply resource_type changes on update (git-sync pull) — Fixes GIT-932 (#10277)
* fix(resources): apply resource_type changes on update (git-sync pull) The OpenAPI spec and generated CLI client both declare `resource_type` on EditResource, but the backend's EditResource struct omitted the field, so serde silently dropped it and update_resource never changed a resource's type. Switching a resource between two types in git and pulling back into the workspace therefore left the workspace out of sync with git (GIT-932). Add resource_type to EditResource and persist it in the UPDATE. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(resources): trim resource_type regression comment to durable rationale Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6de4ec0f66 |
chore(main): release 1.767.0 (#10268)
* chore(main): release 1.767.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.767.0 |
||
|
|
e41440b344 |
feat(ai-chat): email triggers in flow/script chat + trigger-intent eval guards (WIN-2228) (#10267)
* test(ai-evals): guard implicit trigger/schedule intent in flow chat Investigation of WIN-2228 (does flow AI chat understand it should create a flow AND its associated triggers): the flow-editor chat already exposes create_schedule and create_trigger (10 kinds), both confirmation-gated, and an A/B eval shows the model already recognizes IMPLICIT trigger intent reliably (12/12 across two new cases on the current prompt) without naming a "schedule" or "trigger". Add two ai_evals flow cases that phrase the trigger intent implicitly, to guard that recognition against future prompt/tool regressions. These are not redundant with the existing explicit cases (flow-test15/16): a trial system prompt addition that spelled out a deployment prerequisite regressed the HTTP case from 6/6 to 2/6 (the model deferred instead of creating the trigger), which these cases caught. No prompt change ships: the addition showed no measured benefit over baseline and the fuller version regressed behavior. Fixes WIN-2228 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): support email triggers in flow/script create_trigger The chat's create_trigger tool exposed 10 trigger kinds but not email, even though the backend supports email triggers and the chat's open-resource drawer was already wired for them (CreatedResourceActionDrawers, the 'email' CreatedResourceTriggerKind). So when asked to make a flow run on incoming email, the model had no email kind and substituted an HTTP trigger it mislabeled as email. Add email as a create_trigger kind (generator + regenerated zod schema + triggerConfigs → EmailTriggerService.createEmailTrigger). Email triggering only works once an instance superadmin has stood up an SMTP server and set the `email_domain` global setting, so guard the create path: read `email_domain` (readable by any authed user; returns null when unset) and, when it is not configured, return role-aware setup guidance instead of a failing create — pointing a superadmin to Instance settings and a regular user to ask a superadmin, both with the docs link. When configured, create the trigger and report the resulting inbound email address. userStore and the email-address helper are lazy-imported so the chat tools module does not drag in the heavy $lib/stores graph at load. Guarded by unit tests for both branches (shared.test.ts) and an ai_evals case (flow-test19); the model now calls create_trigger(kind=email) 3/3 on a natural "run when an email is received" prompt. Fixes WIN-2228 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): address codex review on email trigger + eval guards - [P1] Default `workspaced_local_part` on the email trigger request body before it is sent, not only when formatting the success address. The column is BOOLEAN NOT NULL, so a request omitting it (the model may) was rejected by the backend. Assert the defaulted `false` in the happy-path unit test. - [P2] Tighten the implicit-intent eval guards so they validate the requested configuration, not just tool selection + path prefix: flow-test17 now checks the cron time (07:30) and UTC timezone; flow-test18 checks kind=http, POST method, no auth, and the route path. Cases still pass 9/9 (sonnet). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ad53673a28 |
fix(ai-chat): improve resource-type search tool description and scoring (#10272)
The global-mode search_resource_types tool described its query as a substring match, but the backend does semantic embedding search. Rewrite the description so the LLM sends natural-language intent instead of literal name guesses. Apply the top-score trim to query_resource_types so a strong match isn't diluted by weakly-related types that merely clear the 0.75 similarity floor. The trim was previously inlined only in query_hub_scripts; extract it into a shared trim_to_top_score helper, use it from both search paths, and add a unit test pinning the 5% cutoff boundary. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0819641f3a |
feat(ai): improve data-pipeline building in AI sessions (prompt + evals + e2e) (#10270)
* test(ai_evals): pipeline coverage for AI sessions + editor e2e Add a complex incremental DuckLake pipeline case, harden the two-node case, and encode the declarative pipeline contract (`-- on` triggers, `-- materialize` + bare SELECT) in the pipeline judgeChecklists so the LLM judge stops false-negativing correct nodes. Add a deterministic Playwright e2e that seeds annotated pipeline scripts and asserts the /pipeline/<folder> editor derives the lineage DAG. Fixes WIN-2229 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai): steer pipeline chat to DuckDB+materialize and warn on missing storage The pipeline authoring prompt (getPipelinePrompt, used by the global/session chat and the /pipeline editor) was neutral on language choice and said nothing about storage readiness. Default it to duckdb materializing into DuckLake unless the work specifically needs postgres/data-tables or bun/python, and add a storage prerequisites section: a DuckLake pipeline needs workspace object storage + a DuckLake catalog, so warn when none is configured and give role-appropriate next steps (admin: workspace settings; others: ask an admin). Drafting is not blocked. A/B on sonnet (global pipeline cases): no regression, +639 finalContext tokens. Fixes WIN-2229 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai_evals): address review - e2e teardown, async-edge note, merge-mode hedge Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(ai): address review nits - drop phantom list_ducklakes tool ref, trim narration comments Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai): add list_ducklakes chat tool for pipeline storage readiness The pipeline counterpart to list_datatables: lists the workspace's configured DuckLake catalogs so the chat can detect the storage prerequisite before building a DuckLake pipeline and warn with role-appropriate next steps when none exists (drafting stays unblocked). Wired into the global tool set and referenced from the pipeline authoring prompt. In the eval run all three pipeline cases called it unprompted with no build regression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(ai): fix DuckDB annotation syntax in duckdb-default section (-- not //) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
07d4b674f1 |
fix: manual resource type sync fetches from hub first, cache as fallback (#10269)
* fix: manual resource type sync fetches from hub first, cache as fallback The superadmin "Synchronize resource types" endpoint (POST /api/settings/sync_cached_resource_types) was cache-first: it read the on-disk hub_rt cache and only fell back to the hub when no cache file existed. Since that cache is refreshed by a daily cron, a newly-published hub type could not be pulled on demand, the button replayed the stale cache and reported "Synced 0", so the type never landed in the admins workspace. The manual endpoint is now hub-first: it fetches the live list and upserts it into admins, falling back to reading the on-disk cache only when the hub is unreachable (airgapped install / network error), logging which path it took. The startup/offline sync in main.rs (SYNC_CACHED_RT + the cache-rt cron) stays cache-based and owns writing the cache, so this endpoint never touches it. Adds an optional `name` query param: when a specific type is requested and is still absent from the hub after syncing, the endpoint returns an explicit not-found instead of a silent "Synced 0". The three not-found frontend call sites (ResourceForm, AppConnectInner, ApiConnectForm) thread the type name through SyncResourceTypes; the global instance-settings button stays name-less. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: run whole-list sync before the targeted not-found check, word 404 by source Address review nits: the optional `name` check ran before the upsert loop, so a `?name=<absent>` request skipped the whole-list refresh; move it after the loop so the sync always happens. Also word the not-found 404 by source, the cache-fallback path (hub unreachable) no longer claims it checked the hub. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2318481f4f |
fix(embeddings): retry on failed init instead of disabling for a day (#10266)
* fix(embeddings): retry on failed init instead of disabling for a day The embeddings init task treated failure and success identically, so a transient error at startup left the embeddings DB uninitialized and every /query_hub_scripts and /query_resource_types call returned "Embeddings db not initialized": - If ModelInstance::new() failed (a transient HuggingFace/network error surviving its own 5 download retries), the spawned task exited for good and embeddings never came up until the next process restart. - If update_embeddings_db (hub fetch + fill_db) failed, the refresh loop still slept the full HUB_EMBEDDINGS_PULLING_INTERVAL_SECS (default 24h) before retrying. Wrap model init in a retry loop, and make update_embeddings_db report success so the refresh loop backs off by HUB_EMBEDDINGS_RETRY_INTERVAL_SECS (default 60s, env-configurable) on failure instead of the full pulling interval. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(embeddings): bound retry backoff to pulling interval A fixed 60s retry loop was fine for the transient case but spun forever in an environment where init can never succeed (air-gapped instance with embeddings left enabled): an error log + HuggingFace fetch storm every ~60s indefinitely, a behavior regression versus failing once and going quiet. Decay the retry with exponential backoff capped at HUB_EMBEDDINGS_PULLING_INTERVAL_SECS, resetting on success. A transient blip still recovers within ~60s; a permanently-broken env settles into ~1 attempt per pulling interval (default: 1/day). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
abd659925d |
chore(main): release 1.766.2 (#10265)
* chore(main): release 1.766.2 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.766.2 |
||
|
|
7e2f1afffb |
fix(python,windows): cross-platform cross-process wheel-install lock (#10264)
* fix(python,windows): cross-platform cross-process wheel-install lock The advisory lock that serializes concurrent uv installs into a shared Python wheel-cache dir was gated `#[cfg(unix)]` and used `nix::fcntl::flock` directly, so on Windows there was no cross-process serialization at all. Multiple agents running as services on one Windows host share a single per-user cache dir (`.../Temp/windmill/cache/python_<v>/`); when several jobs install the same package at once their uv processes clobber each other's atomic renames, surfacing as "no .dist-info directory", "RECORD ... cannot find the file specified", and "failed to rename ... os error 2" install failures. Replace the unix-only flock with `fs4`'s cross-platform advisory lock (flock on unix, LockFileEx on windows). Unix behavior is unchanged (same flock syscall, whole-file, released on handle close/process death); Windows now gets a real per-package cross-process lock so co-located agents serialize their installs instead of corrupting the shared cache. Fixes WIN-2225 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(worker): drop now-unused nix `fs` feature The `fs` feature was only pulled in for `nix::fcntl::flock`, which the previous commit replaced with `fs4`. Remaining nix usages need only `user` (plus the workspace-inherited `process`/`signal`). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b282cd9f5a |
chore(main): release 1.766.1 (#10263)
* chore(main): release 1.766.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.766.1 |
||
|
|
2d24b3ac49 |
fix(jobs): enforce self_approval_disabled on the UI resume path (#10262)
* fix(jobs): enforce self_approval_disabled on the UI resume path The "Resume" button in the run detail UI calls the resume_suspended endpoint, whose owner shortcut skipped the approval-condition checks entirely. A flow owner/operator who triggered the run could therefore self-approve despite self_approval_disabled, unlike the owner endpoint which enforces it. Only admins should bypass self-approval. - Extract require_not_self_approval and enforce it before the owner shortcut in resume_suspended and can_approve_step (button visibility), matching resume_suspended_flow_as_owner. - Persist approval_conditions when self_approval_disabled is set even without user_auth_required, so the restriction is not silently dropped at the resume boundary for raw-flow/CLI authors. Fixes WIN-2223 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(jobs): keep self-approval capability-based on the secret path; docs/tests Scope the self_approval_disabled enforcement to identity-based resume boundaries only. Possession of the full HMAC resume URL is the authorization on the secret path (the URL is disclosed only to intended approvers, e.g. when a step returns it), so resume_suspended_job intentionally keeps skipping approval conditions and token-only (anonymous) resumes on resume_suspended are not gated either. The logged-in owner/operator self-approval fix stays. - Add extract_approval_conditions helper (WAC vs classic) reused in resume_suspended. - Update can_approve_step doc to reflect that self_approval_disabled bars the triggerer before the owner shortcut (codex nit). - Reword new test comments to state the invariant, not prior behavior (codex nit). - Add test_self_approval_disabled_without_user_auth_required covering the persistence + authenticated self-approval check for a non-owner triggerer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
26fcc93d4c |
chore(main): release 1.766.0 (#10247)
* chore(main): release 1.766.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.766.0 |
||
|
|
ecb1a92070 |
fix(copilot): stop write_flow forcing rawscript code into nested JSON (#10260)
* fix(copilot): stop write_flow forcing rawscript code into nested JSON The global-chat write_flow tool made the model embed rawscript bodies inside the modules JSON string, so code had to survive three levels of escaping (tool arguments -> modules string -> content string). Models routinely mangled the quotes/newlines and flow creation failed on the first tries. Bring write_flow to parity with flow mode's set_module_code escape hatch: detect rawscript modules left empty or as inline_script placeholders and tell the model to fill them via set_flow_module_code, add a code-escaping hint to the JSON parse error, and update the guidance to keep code out of the modules structure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(copilot): only warn on saved write_flow; add regression tests Address review: writeFlowDraft reports conflicts/persistence errors as {success:false} rather than throwing, so the empty-body warning must be folded into the JSON result only on a successful save — otherwise the model is told to set_flow_module_code on a flow that was never saved (stale or nonexistent draft). Add core.test.ts coverage for the empty-body warning (top-level, nested, preprocessor, failure; populated suppressed), the no-warning-on-failed-save path, and the malformed-JSON escaping hint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(copilot): warn on patch_flow_json inline_script placeholders Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(ai-evals): add global case for quote-heavy inline flow code Exercises write_flow creating a rawscript whose body is multi-line and quote-heavy (the scenario the write_flow fix targets), so the global-mode A/B can measure that code lands out-of-band via set_flow_module_code rather than being escaped into the modules JSON string. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(copilot): resolve inline_script placeholders in global flow writes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(copilot): condense patch_flow_json warning comment Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(copilot): soften write_flow guidance to inline by default Benchmarks showed the aggressive "empty content + set_flow_module_code for any multi-line/quoted body" guidance pushed even capable models onto the multi-round-trip fill path, inflating per-iteration overhead with no reliability gain when inline escaping would have succeeded. Default to inlining and reserve the empty+fill escape hatch for bodies that are genuinely hard to escape or when a write_flow call returns a JSON parse error — the case that actually benefits escaping-prone models. The warning, parse-error hint, and set_flow_module_code recovery path are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai-evals): drop global quote-heavy inline-code case The manual global-mode A/B (Sonnet, Gemini 3 flash/pro, GPT-4o) showed no pass-rate delta: the GPT-5 inline-escaping failure this change targets does not reproduce on any available model, so the case guards nothing measurable. Keep the unit tests as the regression guard instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Guilhem Lemouel <guilhemlemouel@gmail.com> |
||
|
|
c23f1880d2 |
show preview chip on write tools, not open_preview (#10261)
* fix(ai-sessions): show preview chip on write tools, not open_preview Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-sessions): render preview chip label in UI font, not mono Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-sessions): cast RowIcon to IconType for Button startIcon RowIcon has a required `kind` prop, so it is not assignable to Button's `IconType` (Component<{ size?: number }>). The `props` field carries the runtime prop, so cast the icon to satisfy the type check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
1da664fc89 |
open_page opens resource/variable edit drawers when asking user to act (#10258)
* feat: open_page opens resource/variable edit drawers when asking user to act Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: clear handled drawer hash when navigation leaves the target Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: force preview reload when open_page re-targets the tab's current URL Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d03810276c |
remove collaborator presence badge from session preview (WIN-2222) (#10259)
* fix(ai-sessions): hide collaborator presence badge in script preview The workspace-wide presence layer (MultiplayerMenu) auto-connects under an EE license and broadcasts each user's window.location.pathname. The <Awareness /> badge in ScriptBuilder's header renders anyone whose path matches the current pathname. In the AI session preview the editor is embedded in the /sessions page, so presence collides on that single coarse URL and paints a meaningless badge (often the current user themselves) on a brand-new draft where no one is co-editing. Suppress the badge whenever ScriptBuilder runs inside the session pane, reusing the existing inSessionPane context flag. Fixes WIN-2222 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-sessions): hide presence badge in flow/app/raw-app session preview too Extend the same inSessionPane guard to FlowBuilder, AppEditorHeader and RawAppEditorHeader so the spurious collaborator presence badge is hidden in every editor embedded in the session pane, not just scripts. The aiChatManager context is injected only by SessionEditorTarget, so the guard is a no-op in the standalone editors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
564b93b968 |
(cloud) restrict folder creation, sharing and group creation in demo workspace (#10257)
* feat: restrict folder creation, sharing and group creation in demo workspace Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: guard update_folder sharing bypass and gate folder editor/share ACL controls in demo Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: guard remove_owner grant path and gate alternate folder-create controls; keep pure-revoke controls enabled Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8310e46b19 |
(windows) lean worker-only build, stop compiling the amqp trigger (#10251)
windmill-trigger-amqp does not compile on Windows: tokio-reactor-trait only implements reactor_trait::Reactor for its Tokio type under #[cfg(unix)]. This broke two Windows CI jobs since the amqp trigger landed (#10230): the ee_windows worker build (via the amqp_trigger feature) and, because the crate is a default workspace member, the backend-test-windows job (`cargo test --all` compiles every member regardless of features). The amqp trigger is a server-only feature never run on Windows workers, so the fix is to stop compiling it on Windows rather than port its reactor. Worker binary (ee_windows): replace the ce_core+ee_core bundle (every trigger + all server-only features) with a worker-only worker_windows_core. A non-agent worker still runs the full windmill-api on localhost for its own operations (main.rs run_server, under `if !is_agent`) and jobs call back into it via the wmill client, so keep every feature the worker's own runtime path or its jobs touch, and drop the rest. Kept: languages, parquet, quickjs, enterprise/license, prometheus, otel, jemalloc, AI-agent execution (windmill-worker/mcp + windmill-store/mcp client and OAuth-MCP refresh, windmill-worker/bedrock for direct AWS Bedrock), OIDC Vault secrets (openidconnect), instance-SMTP email — critical alerts and the error-handler send endpoint (windmill-api/instance_smtp), OAuth refresh (oauth2 — reload_base_url_setting populates OAUTH_CLIENTS, get_value_internal refreshes tokens in the worker's internal API server), inline/preview runs (run_inline — jobs call /jobs/run_inline/*). Dropped: all *_trigger/kafka/nats/sqs listeners plus static_frontend, stripe, embedding, zip, the MCP gateway (windmill-api/mcp), the server Bedrock proxy route (windmill-api/bedrock), and cloud (runtime-gated on CLOUD_HOSTED, never true self-hosted). Split windmill-api's smtp feature: the send_email_with_instance_smtp endpoint (error-handler failure emails) only needs windmill-common's rustls sender, but the smtp feature also bundled the inbound email trigger's openssl + mail-parser + windmill-trigger-email. Add instance_smtp = ["windmill-common/smtp"] gating just the endpoint; smtp now includes it. The worker uses instance_smtp, avoiding openssl (which broke the ee_windows check step) and the email-trigger crate. backend-test-windows: the Windows binary is worker-only, so test the crates a worker runs (windmill-worker/-common/-queue) via -p instead of `cargo test --all`. --all compiled every workspace member regardless of features — pulling in the amqp crate (which does not build on Windows) and linking the whole windmill-api integration-test suite, whose combined size overran the runner disk (LNK1180). Also unset the setup-rust-toolchain default RUSTFLAGS=-D warnings for this job so cross-platform dead-code (cfg(unix)-only helpers unused on Windows) does not fail the run; hygiene stays enforced on the Linux CI and the build_windows_worker_ release build. Full-workspace coverage runs on the Linux CI. Also drop the redundant `mkdir frontend/build` from the Windows worker workflows and stub openapi-deref.json alongside the .yaml to avoid embedding ~2.5MB of openapi spec the worker never serves. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0508cddf0a |
feat(sessions): ship AI sessions as beta with legacy-chat opt-out (#10242)
* feat(sessions): ship AI sessions as beta with legacy-chat opt-out The wm_dev_global_ai dev flag becomes a beta opt-out: sessions are on by default and gate.ts reads wm_sessions_beta_optout instead (same isGlobalAiEnabled() name, all call sites unchanged). A slim Alert-info banner under the session chat lets users switch back to the legacy docked chat (plus a GitHub feedback shortcut), a mirror banner in the legacy chat reactivates sessions, and /sessions visited while opted out offers reactivation instead of dev-flag instructions. Both toggle directions are recorded on the existing ai_chat_usage telemetry channel (mode sessions_beta_optout/optin) before the page reload. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(sessions): keep dev-only routes off the beta gate /global_drafts and /dev/session-tree were gated on the sessions gate to hide dev tooling; the beta inversion would have shipped them enabled by default. Gate them on dev builds (import.meta.env.DEV) instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(sessions): exempt operators from the sessions beta The operator sidebar has no Workspace/Sessions switch, so gating the docked chat on the beta left its Ask AI button toggling an unmounted pane. Operators keep the legacy chat (without the beta banner, whose Activate would strand them on /sessions). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(sessions): navigate even when the opt-out write fails A throwing localStorage (quota, private browsing) made the banner buttons silent no-ops. Navigate regardless — the reload showing the unchanged mode is the honest feedback — and skip the toggle telemetry since no toggle actually persisted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(sessions): migrate toggle telemetry to feature_usage; gate /sessions for operators Main replaced the log_chat endpoint with the allow-listed log_feature_usage channel, so the toggle events move to logFeatureUsage('ai_session', 'beta_optout'/'beta_optin') — the buffer's pagehide flush + keepalive fetch carry the request across the hard reload, so the await/cap plumbing goes away. The two kinds are added to the backend allow-list. Operators reaching /sessions by direct URL now get a "not available for operators" screen instead of bypassing the layout-level exemption. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(sessions): open legacy pane on opt-out; banner matches composer column Review nits + polish: opting out now persists ai-chat-open so a fresh profile lands in a visible legacy chat (with its reactivation banner) instead of a bare workspace page; the operator /sessions screen's button now actually opens the Ask AI pane (operators have no sidebar toggle); the beta banner is a rounded inset bar sharing the hosting chat's composer column width. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
68b1fcc5cd |
fix: prevent u16 underflow in suspend count causing permanent flow deadlock (#10256)
When extra resume_messages arrive concurrently and resume_messages.len() exceeds required_events, the u16 subtraction wraps to ~65535, which is written as the suspend counter and permanently deadlocks the flow waiting for events that never arrive. Use saturating_sub so it clamps to 0 instead. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5685981c99 |
fix(tutorials): repair broken frontend tutorials after UI redesigns (#10255)
* fix(tutorials): repair broken frontend tutorials after UI redesigns Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(tutorials): wait for New menu anchor and drop vestigial async Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
703744fb8b |
feat(ai-sessions): show item preview cards for tools (#10254)
* feat(ai-sessions): show item preview cards for create/update/open-preview tools Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-sessions): only show open_preview card when the preview actually opened Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-sessions): render preview card as a chip on the tool-call header row Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d2c5d6f4b4 |
feat: make content search a full CE feature (#10252)
Content search (the `#` mode of the home-page Ctrl+K search, which searches scripts/flows/apps/resources by content) was capped on CE to 10 scripts and 3 each of flows/apps/resources, with an "EE feature" warning in the UI. It is now a full CE feature: the CE result caps are lifted to match the previous EE limits (10000 scripts, 1000 each of the rest) and the EE warning is removed. Fixes WIN-2218 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
380cf752ca |
fix(prompts): prefer Bun over Deno for TypeScript runtime selection (#10253)
Make the AI prompting instructions explicitly pick Bun as the default and
preferred TypeScript runtime, and treat Deno as the exception (only when a
script specifically requires the Deno runtime: Deno stdlib or deno.land URL
imports).
Previously the `write-script-bun` and `write-script-deno` skill descriptions
both read as equally valid TypeScript defaults ("MUST use when writing
Bun/TypeScript scripts" vs "MUST use when writing Deno/TypeScript scripts"),
giving no signal on which to choose for a generic TypeScript request.
Source-of-truth edits (system_prompts/utils.py LANGUAGE_METADATA,
languages/bun.md, languages/deno.md, cli/src/guidance/core.ts) then
regenerated via system_prompts/generate.py into the auto-generated skills,
prompts, and cli/src/guidance/skills.gen.ts.
Fixes WIN-2220
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
b948efd3c8 |
fix: accept ssh/scheme-less git repo urls and $var: refs in app repo resolution (#10246)
* fix(git-sync): accept ssh/scheme-less repo urls and $var: refs in app repo resolution Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(git-sync): interpolate repo urls at github-call sites only, not in persisted markers Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: update ee-repo-ref to 183f78b3ee193d6b5e55fd453c570f94a12c8b13 This commit updates the EE repository reference after PR #681 was merged in windmill-ee-private. Previous ee-repo-ref: 9bc5018f68edf3a9f256ef6315ad6ddf4fba3a45 New ee-repo-ref: 183f78b3ee193d6b5e55fd453c570f94a12c8b13 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> |
||
|
|
50f1375f18 |
chore(main): release 1.765.0 (#10223)
* chore(main): release 1.765.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.765.0 |
||
|
|
ec6324409d |
fix(s3): support instance-policy credentials in object storage tests (#10238)
* fix(s3): cache ambient aws credentials and surface credential chain errors Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCczeVBDhDnLXWqxPWw6od * fix(frontend): clarify that object storage connection tests run on the server Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCczeVBDhDnLXWqxPWw6od * fix: render test connection tooltip in popup and harden cache test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCczeVBDhDnLXWqxPWw6od * better doc * fix(frontend): correct tooltip wording, tests run from the executing worker Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCczeVBDhDnLXWqxPWw6od * fix(s3): single-flight ambient credential provider creation * fix(frontend): clarify object storage tests run on the server process * fix(frontend): make instance object storage test tooltip provider-neutral --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
2ce21c9ef8 |
feat(git-sync): enable per-item promotion mode on dev workspaces (#10205)
* feat(git-sync): enable per-item promotion mode on dev workspaces Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style: keep unrelated git-sync Alert copy at its original wrapping Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): fall back to parent_path on empty deploy path + bump ee ref computeGitSyncDeployBranch used ?? so a backend-serialized empty path (rename out of the repo filter) skipped the deploy branch and could commit to the tracked base; use || to fall back to parent_path like the backend. Bumps ee-repo-ref for the single-object promotion_open_prs fix (windmill-ee-private#679). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): route dev-promotion non-branchable objects off the tracked base user/group objects (and any unresolvable ref) returned null in promotion mode, so a dev-workspace deploy pushed them straight to the parent's tracked branch. Fall back to the dev's env-label branch instead; the backend opens no PR for them (isolated, not promoted). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(git-sync): dev-workspace promotion via a toggle on the inherited repo A dev workspace reuses the single repo it inherited from prod: a 'Promote to prod via Git' toggle flips it between sync mode (deploys to the dev branch) and promotion mode (per-item wm_deploy/** PRs to prod), with a per-item/per-folder sub-toggle. Removes the redundant separate-promotion-repo setup for dev workspaces. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(git-sync): dev-promotion regression test + widen git_sync_e2e path filter Adds a CLI integration case covering dev-workspace promotion (script -> wm_deploy branch; user/group -> env-label branch, main never touched). Widens the git-sync-test.yml relevance filter to the deploy-branch derivation, git-sync guard, and CLI git-deploy files so the e2e suite runs on PRs like this one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): gate dev promotion toggle on EE, fix card mode + workflow path filters Codex review: (1) show the dev promotion toggle only under an active EE license and revert the optimistic save if the backend rejects it; (2) derive the dev card's display mode from use_individual_branch so promotion copy shows in promotion mode; (3) mirror the new relevance paths into the workflow's top-level push/pull_request filters so it actually triggers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): only use the single-card dev promotion UX when the dev has one repo Codex review: an attached dev workspace keeps its own repositories rather than inheriting prod's. Gating the single-card + toggle + hidden-secondaries UX on repositories.length <= 1 makes a multi-repo attached dev fall back to the normal layout, so no active repo is hidden and an unrelated repo isn't presented as prod's promotion target. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): runtime EE-plan gate for promotion mode, consistent with auto-pull/PR Codex review: promotion mode only had the CE compile rejection, while auto-pull and PR creation runtime-gate on the active plan (check_git_sync_ee_license). Add check_promotion_license and call it from both edit_git_sync_config and edit_git_sync_repository, plus the matching CE rejection on edit_git_sync_config so the two endpoints are symmetric. Promotion is now gated like every other git-sync EE setting. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): dev promotion must reuse the parent workspace's repository Codex review: repository count doesn't prove a dev inherited prod's repo — an attached dev keeps its own. check_dev_promotion_targets_parent_repo resolves the promotion repo's URL and rejects enabling promotion unless it matches one the parent (prod) tracks, so branches/PRs can't target an unrelated repository. Called from both git-sync edit endpoints. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): dev promotion save-time check uses shared parent-repo matcher (url+branch) Delegates to windmill_common::git_sync_ee::dev_promotion_target_matches_parent so the settings gate and the deploy-time safety net share one url+branch identity check. Bumps ee-repo-ref for the EE deploy-time enforcement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump ee-repo-ref for private resolve_repo_url_and_branch Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump ee-repo-ref for promotion-target matcher authz doc Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(git-sync): bump hub scripts to gitsync-cli versions, fix promotion tooltips Point LATEST_GIT_SYNC_SCRIPT_PATH (28790 -> 28796) and GIT_SYNC_PULL_SCRIPT_PATH / gitInitRepo (28789 -> 28795) at the hub versions pinning windmill-cli@1.763.1-gitsync.0, which carries the dev-workspace promotion routing. Slugs unchanged, so the GitHub-App token check and hub script cache are unaffected. Tooltips: enabling promotion pushes a PR-ready wm_deploy/** branch; Windmill only opens the pull request itself when automatic pull requests are enabled. Reword both toggles to stop promising a PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): dev promotion mirrors to the env-label branch, PR toggles exclusive by branch type Bump ee-repo-ref for the dispatcher changes: a promotion dev's deploys now also push to its env-label branch (one extra mirror job per batch, users/groups mirror-only), and `fork_open_prs` no longer applies to a dev in promotion mode where `promotion_open_prs` governs. Frontend: the fork-PR toggle tooltip states its actual coverage (wm-fork/** and the dev branch of a dev workspace) and that a promotion dev's own pull request toggle takes over for wm_deploy/** branches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): reject dev promotion on pre-28796 pinned sync scripts An older pinned sync script bundles a CLI that force-disables per-item branches on every fork, so enabling promotion on a dev workspace with such a pin would silently keep deploying to the env-label branch. Both git-sync edit endpoints now reject the combination with an actionable error; the EE dispatchers (via ee-repo-ref bump) demote inherited configs to promotion-off semantics so markers, branch keys and the mirror match the branch the CLI actually pushes. Roots and auto-managed repositories are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): serialize dev promotion toggle saves The promotion and per-folder toggles persist immediately via whole-repo saves; leaving them interactive while one is pending lets rapid flips race, and the earlier save (enabling runs extra backend checks) can commit last, silently reversing the state the UI shows. Both toggles now disable while a save is in flight. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(git-sync): lock auto-PR toggle during promotion save, rename-out branch routing Frontend: the automatic-PR toggle is revealed by the promotion toggle's in-flight save; an edit made mid-save was absorbed into the saved baseline without reaching the backend. It now disables during that save. EE (ee-repo-ref bump): dispatcher debounce/concurrency keys and PR markers follow the CLI's parent_path fallback for rename-out items, so their wm_deploy/** branches debounce per-branch and open their PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(git-sync): condense comments to durable constraints Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 8bf73f803158bcbf7b8d55a36f4a1ebfcc1bbcd9 This commit updates the EE repository reference after PR #679 was merged in windmill-ee-private. Previous ee-repo-ref: c2cd718cb53d234f909f485bd7cd43ed9605ffd1 New ee-repo-ref: 8bf73f803158bcbf7b8d55a36f4a1ebfcc1bbcd9 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> |
||
|
|
9739d5a2c2 |
feat: unified read-only diff chat tool (drafts, fork vs parent, search) (#10211)
* feat(frontend): unified `diff` chat tool with cached snapshot, fork mode, and search One read-only global-chat tool for every comparison: drafts vs deployed (workspace index + per-item unified patches over stable YAML), deployed fork vs parent workspace (against="parent_workspace", sharing the fork banner's compareWorkspaces fetch through a single-flight store), and a literal grep over changed diff lines. Multi-file raw apps split into per-file text patches with folder-style index children and per-file reads. Patches are materialized once into a per-workspace cache keyed on draft created_at / comparison ahead-behind markers and the workspace drafts version, so repeated queries never refetch unchanged content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai_evals): teach the mock draft backend what the diff tool reads The diff tool reads drafts through the get_draft overlay, the drafts listing's draft_only flag, and per-row created_at change markers — none of which the benchmark mock modelled (fixed timestamp, always draft_only, overlay ignored), so in evals every draft looked absent and the model looped to max turns. Mirror production: monotonic deterministic created_at bumped per upsert, draft_only computed against the deployed stores, and draft/no_deployed overlays on script/flow/app reads (404-shaped not-found). Also drop the diff case's judge items about conversation content the judge never sees — tool usage is already enforced deterministically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): diff tool reads unsaved editor state instead of going stale The pre-diff flush honors the auto-save toggle (a read-only tool must not persist parked edits), which left a gap: with auto-save off — or after a failed save — the persisted draft the diff reads is stale, and a brand-new editor-only draft looks absent. Item reads now detect unflushed parked edits (hasUnsavedDisabledChanges / failed save state) and diff the in-memory editor value directly, bypassing the snapshot cache (it must only hold persisted state) with an explicit unsaved- changes note; index and search modes warn which items' unsaved edits they exclude. Local values are canonicalized onto the persisted draft shape so they never diff noisily against the deployed side. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(ai): invalidate diff cache the moment any draft write lands The snapshot cache leaned on time windows (5s listing throttle, 15s read reuse) to notice writes it didn't trigger itself — an editor autosave landing between two diff reads could serve the pre-edit patch. The syncer now exposes onAnySaved (fires for landed upserts AND deletes, all keys), and the snapshot subscribes once: a landed write marks exactly that item's patch stale and expires the listing throttle, so the next read refetches regardless of any reuse window. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): reject the diff file arg on single-document items Passing file for a script/flow/classic-app diff was silently ignored and returned the whole patch — an explicit error steers the model to call again without it. Also declares the file arg on the item handlers' signatures it was already flowing through. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): surface empty-file additions/deletions in app diffs An empty file appearing or disappearing produces no text patch, so the per-file split dropped it — a draft whose only change was such a file read "unchanged". Presence changes now keep their added/deleted entry (patch '', 0 lines), render as "(empty file)" in summaries, and a file read states the presence change instead of an empty window. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): include classic-app drafts in the diff index and fix ++/-- search itemTypeForKind now maps classic `app` draft rows to the chat app type (mirroring the read path, which already pairs app/raw_app), so their diffs materialize in the index and search instead of reporting "not addressable". Changed-line search is hunk-aware: `---`/`+++` file labels only occur before the first @@ marker, so a changed source line like `++counter` (rendered `+++counter`) now matches instead of being mistaken for a label. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): mask every variable value in chat diffs; compare classic apps value-to-value Variable VALUES never reach a tool result — the chat-wide invariant read_workspace_item enforces, not just for secrets. Draft-mode diffs mask both sides with a placeholder pair that still marks WHETHER the value changed; fork-mode masks at fetch (and still never decrypts); item reads carry an explicit note. The former secret-only flag is now valueMasked. Classic-app drafts hold the bare grid value while the deployed row nests it beside summary/policy — diffed raw, a one-field edit read as a whole-document rewrite. Both sides now reduce to { value } via classicAppDraftValue (pure, unwraps legacy wrapped drafts), which also cleans the CompareDrafts drawer for classic apps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): honest secret-draft reporting, classic-app metadata split, glob-safe file subjects A secret variable's sides are both masked upstream, so an empty patch cannot prove the value is unchanged — such drafts now report "cannot be compared; may differ" (valueUncomparable) instead of "matches deployed", in the index and item reads. Classic-app drafts mirror summary/draft_path into the bare grid while the deployed row keeps summary as a column: sides now reduce to {summary, value} via classicAppDraftParts, applied to both sides, so a summary edit diffs as one and draft-only markers never pollute the grid diff. Raw-app search subjects strip the file key's leading slash so slash-anchored globs like f/x/*.tsx match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): classic-app local edits, comparison-relevant fork fields, conflicts as unflushed The chat app type spans two draft kinds: item mode now flushes and probes both raw_app and classic app keys, and the flush sweep includes classic-app editor cells (kept out of GLOBAL_DRAFT_KINDS so clearGlobalDrafts never clears an open classic editor). Fork projections gain the fields the backend comparison counts that getItemValue drops: flow schema (with a taxonomy-agnostic inline-hash strip) and resource-type description/format_extension/is_fileset. Folder display_name is not exposed by the API's Folder type, so it cannot be projected. A conflicted save leaves its payload parked with state 'none', so index/search now count conflicts among unflushed paths and say so in their warnings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): staged app renames diff as path; flush classic-app cells at their real keys A staged rename (draft_path) changes where deploy lands an app, so both app kinds now compare `path` on both sides — a rename-only draft diffs instead of reading "matches deployed". classicAppDraftParts returns the staged path separately from the grid. Item mode resolves each draft kind's own storage path and additionally asks the listing which row owns a friendly/renamed path — a renamed classic app's cell lives at its ORIGINAL storage path, which only the listing knows — so pending/failed/auto-save-off edits are flushed and probed at the real keys. The appDiffSides rationale comment is compressed to the repo's four-line limit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): never claim folder parity the API cannot prove folder.display_name exists only as a DB column — no folder endpoint returns it — so an identical projection cannot prove a fork folder matches its parent. Fork index and item reads for folders now say the display name is not exposed and may be what differs, instead of "content matches parent". Exposing the field on getFolder is a backend follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): gap-free patch pagination; forced-fresh comparisons never join older fetches When the char backstop cut inside a patch window, the continuation offset still pointed past the requested window — silently skipping the undelivered lines forever. windowPatch now cuts at the last complete line and continues exactly there (a single over-budget line is delivered truncated and stepped past so pagination always advances). fetchWorkspaceComparison treats an in-flight request as being as old as its start: maxAgeMs now gates joining it, so a freshness-forced post-mutation read (maxAgeMs 0) always issues its own fetch instead of adopting a tally that began before the mutation, and a superseded request can no longer clobber a newer cached result. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): generation-ordered comparison writes; path-only fork reads for enum-less kinds Concurrent comparison requests can share a Date.now() value, letting a superseded request's late result overwrite a newer one and be reused for 30s — cache writes are now ordered by a monotonic request generation. Test pins the same-millisecond race with the newer request resolving first. Fork comparison kinds outside the chat type enum (folder, resource_type, …) were listed and even advertised as readable but no call could reach them: a fork item read without `type` is now a path-only wildcard (ambiguous paths list their kinds and ask for type), messages label entries by their comparison kind, and pending index lines for enum-less kinds advertise the path-only read. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): grid-based wrapper detection, comparison invalidation on mutations, multi-kind wildcard reads, honest hidden-diff summaries The classic-app wrapper heuristic keyed on metadata keys the editor mirrors into every bare grid — a grid with a component named `value` was reduced to that component. `grid` presence is the discriminator: a bare App always has it, a legacy wrapper never does. invalidateWorkspaceDrafts now also drops cached fork comparisons for the workspace, so the FIRST post-deploy fork read cannot reuse a banner-prewarmed pre-deploy tally (the snapshot-baseline check only covered subsequent reads). Wildcard fork reads return a section per matching kind instead of an unactionable "pass type" for kinds the chat type enum cannot name, and the fork index never summarizes ACL-hidden differences as parity — hidden counts stay directional (a conflicted item counts in both). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): address cubic review batch — invalidation scope, races, edge output Comparison cache: invalidation matches either side of the pair (a parent deploy moves its forks' tallies), fences in-flight requests (no new joins, late results rejected via a per-key generation floor), and the map is LRU-capped. Eviction moves from every drafts-version bump to deploy success only — draft saves never move the deployed tally. Fork snapshots also baseline the PARENT's drafts version. Draft materialization carries a stale-generation token so a save landing mid-fetch discards that run's pre-save result instead of repopulating the invalidated entry; a save/delete also expires the fork cache's hasLocalDraft join. onAnySaved listeners are error-isolated (a throwing listener must not mark a committed save failed) and the pagehide keepalive flush notifies them on dispatch. Output edges: folder fork lines drop the empty parenthetical, and a patch-window offset past the end reports itself instead of an impossible range. The eval case pins the diff call's path argument. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(ai): one fencing primitive per cache instead of per-surface races Review rounds kept finding pairwise races between async producers and invalidation — each patched with its own fence. Replace the class: - diffSnapshot: a per-workspace mutation epoch, bumped by every invalidation. Both reconcilers run a bounded retry loop — joiners re-validate after awaiting, producers refuse to store results whose inputs predate a mutation. Covers in-flight listing adoption and pre-deploy fork tallies in one mechanism. - workspaceComparison: per-WORKSPACE generation floors (either side of a pair). Any request started before an invalidation is fenced from joining and from landing in the cache — including superseded requests the inflight map no longer tracks. Also: delete_workspace_item invalidates comparisons like deploy does (deployed state moved), and empty FILTERED indexes say the filter matched nothing instead of claiming workspace/fork parity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): invalidate comparisons on every direct deploy; keep secret caveat with metadata changes Direct chat deploys (schedule/trigger/resource/variable/app) bypass deployDraftToWorkspace and never evicted cached fork comparisons — the shared deploy tail now invalidates before the fallible draft cleanup. A secret variable whose metadata also changed produced a non-empty patch that silently dropped the value-uncomparable caveat; item reads, the index, and fork sections now keep the caveat alongside the patch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): scope diff caches to the authenticated identity; derive fork freshness from the comparison store An SPA logout/login left workspace-keyed diff caches (per-user drafts, permission-filtered fork patches) readable by the next account — both cache modules now wipe on identity change, with a global generation floor fencing requests started under the previous account. The fork snapshot stamped its own fetchedAt over a comparison that could already be near expiry, compounding the two 30s windows, and survived comparison-store invalidation when draft cleanup failed after a deploy. It now carries the comparison's own fetchedAt/generation and stops reuse the moment the store fences it. Delete-item invalidation moved before the fallible draft cleanup for the same reason. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): fence fork-reconciliation joins across account switches ForkCache lacked the epoch stamp WorkspaceCache carries, so a joiner arriving after an identity change (or any epoch bump landing before it) compared its own post-bump epoch against itself and adopted the old producer's in-flight tally. The cache now records its producer's epoch for the joiner and reuse gates, and an identity change also discards the in-flight reconciliation maps so no cross-identity join exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): surface swallowed fork-side fetch failures; include conflicted editor edits in item diffs The shared getItemValue reads {} for any failed fetch, so a transient API failure on a fork side rendered as a fabricated one-sided diff (or parity when both sides failed). A fork side is only fetched when the comparison lists it as existing, so an empty read now raises and shows as a fetch-error entry. Item reads promised conflicted local edits (the index says so) but the local-override branch only covered autosave-off and failed saves — a conflict silently fell back to the persisted draft. Conflicts now read the in-memory editor value too, with a caveat naming which side is shown either way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): report failed diff materializations as unsearched instead of silently omitting them A side-fetch failure left an index entry with status 'error' and no patch; diff search skipped it and still presented definitive no-match or complete-count results. Failed entries are now listed in a warning naming what was not searched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d6cf1ef987 |
feat: attach text files to chat messages, read on demand via file tools (#10215)
* feat: attach text files to chat messages, read on demand via file tools
* fix: resolve name collisions and sync message files with the transcript
* refactor: render message file chips with the shared context badge
* fix: suffix same-name attachments, sync registry on compaction, bound file bytes
* feat: carry message files across compaction, drop them from the roster
* refactor: merge context, dom and file badges into one wrapping row
* fix: dedupe identical attachments and make badge list keys collision-proof
* fix: name carried files inside the collapsed summary instead of badges
* fix: serialize registry reconciliation and correct the attachment budget
* fix: reserve pending bytes so overlapping reads honor the attachment budget
* fix: share attachment byte budget across concurrent composers
The bottom composer and the edit box are both mounted while editing an
earlier message, but each enforced MAX_CONVERSATION_FILE_BYTES against
only its own staged files plus the transcript. Two attaches near the cap
could each pass independently and overflow the persisted transcript.
Each composer now publishes its staged bytes (committed attachments +
in-flight reads) to the manager, keyed per instance, and the attach-time
budget subtracts every other live composer's stage. A message an open
composer is editing is skipped from the transcript sum since that
composer's stage stands in for it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: charge an edited message at its persisted size until the edit commits
An edit is not committed until send, so the edited message's persisted
attachments return if the edit is cancelled. Substituting only the edit
box's (possibly emptied) stage let the always-mounted bottom composer
claim headroom that vanishes on cancel: remove the near-limit files in
the editor, fill the bottom draft, cancel, and the persisted transcript
overflows MAX_CONVERSATION_FILE_BYTES.
attachmentBytesExcluding now charges a message another composer is
editing at max(persisted size, editor stage), so freed space only
becomes available once the edit actually commits.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: preserve a message file's exact name when a session row clashes on rebuild
syncMessageScoped rebuilds message-scoped rows from the transcript through
collision-suffixing addFiles. Session rows load first (on restore), so one
holding a wanted name pushed the rebuilt message row to a "(2)" suffix while
the persisted prompt still referenced the bare name — get() then resolved the
reference to the session asset and the model read the wrong content.
Free the name from the conflicting session row before the rebuild so the
message row reclaims its exact reference. The rename is in-memory only: it is
deterministic and re-applied on every load, and the session roster is
regenerated live each send, so the session asset stays addressable under the
suffix without a persisted-record update.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: reserve resent files across the edit-resend gap
The edit box unmounts (dropping its staged-byte entry) the instant the user
submits an edit, but restartGeneration then awaits registry sync and beforeSend
before the optimistic bubble lands in the transcript. During that gap the
resent files were reserved nowhere, so the always-mounted bottom composer could
attach into the temporary headroom and the resend would then push the persisted
transcript past MAX_CONVERSATION_FILE_BYTES.
restartGeneration now reserves the resent files' bytes in shared manager state
before the transcript slice; sendRequest releases the reservation once it
installs the bubble (or restores the files to the composer on a pre-install
bail). The reservation bridges the gap so the budget stays honored throughout.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: re-target an in-flight index when a session row is renamed on rebuild
#freeNameForMessageRow renames a restored session row so a same-named message
attachment can reclaim its exact name. But the row's #indexFile, started under
the old name during restore, stamps via #patchFile(oldName, file) — after the
rename that no longer matches, leaving the row stuck 'indexing' so read_file
rejects it and search_files excludes it.
Re-kick #indexFile under the new name when the renamed row is still indexing;
the stale completion then no-ops (its name is gone).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: release resend reservation on local-command sends; surface compaction-orphaned files
Two follow-ups to the message-attachment work:
- A resend edited to /clear or /compact runs the local-command path and returns
before installing a bubble, so the #RESEND_KEY reservation set by
restartGeneration was never released and its bytes stayed charged, blocking
later attachments. Release it on every sendRequest path that exits before
install (via #releaseResendReservation).
- Drop-oldest compaction (summary fallback) removes API messages without a
summary, so a folded message's `## ATTACHED FILES` reference no longer reaches
the model even though the file stays readable. The roster omits message-scoped
files, so the model loses awareness of them. orphanedMessageFileNames() finds
message files whose only referencing message went negative-index, and the
roster now advertises them (summary compaction already carries its own).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: key the resend reservation per send so unrelated sends can't release it
The resend reservation used a single shared key, so a normal or concurrent
sendRequest released it at its own install/early-return even though it didn't
own it — dropping an in-flight resend's reservation and letting attachments
staged before the resend bubble lands under-count against the byte cap.
restartGeneration now mints a per-resend token, reserves under it, and threads
it through sendRequest as resendReservationKey; releases act only on that key.
A send with no token (every normal send) releases nothing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: reserve the whole wanted-name set when freeing session rows on rebuild
Freeing a session row for a message-scoped rebuild suffixed it against current
rows only. With the transcript referencing both notes (2).md and notes.md and a
session row named notes.md, freeing notes.md renamed it onto notes (2).md — also
a wanted reference — so that message row cascaded to notes (3).md while its
persisted reference stayed notes (2).md, and read_file returned the session file.
#uniqueName now accepts a reserved set; the rebuild frees each session clash
clear of the entire wanted-name set, so every message row reclaims its exact
reference regardless of collision order.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: orphan summary-carried files when drop-oldest removes the summary
orphanedMessageFileNames classified summary rows as always-live, but a summary
carries its folded files' reference on its own API message. When summary
compaction succeeds and a later summarization fails, drop-oldest can remove that
API message, yet the files stayed off the roster — so the model lost their
reference even though they remained readable.
The summary display row now tracks its API index (slot 0 at creation, re-based by
drop-oldest); a negative index reads as "counterpart gone" and its files move to
the roster, mirroring user-message orphans. The index is used only for orphan
detection, never as a restart target.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: reserve outgoing file bytes for normal and queued sends too
The resend reservation covered edit/retry, but a normal or queued send has the
same gap: the composer (or queue) clears its files the instant sendRequest is
called, dropping the staged-byte entry, while sendRequest then awaits
regrantLocked()/refreshFolders() before the bubble lands. With a locked or slow
linked folder the composer stays enabled, so a fresh drop can spend the same
headroom and overflow the 5 MB cap once the first bubble installs.
Generalize the reservation: sendRequest mints a per-send token and reserves the
outgoing files' bytes just before attachment upkeep (reusing restartGeneration's
token when present), and releases it on install or any pre-install exit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor: join attachment layers on a stable content-hash id
Sixteen review rounds kept finding the same bug family: a message file's
identity was its display name, joined by hand across the registry, the
transcript, the prompt reference, and the render keys — every same-name
collision or interleaved rebuild made two of those copies disagree.
Give each message attachment a deterministic id, attachedTextFileId(name,
content) — a synchronous pure-JS hash (works on plain-HTTP deployments) whose
exact value is pinned by test, since persisted transcripts reference it. The
prompt and roster list the id, the file tools resolve id-first (bare names
remain a fallback for legacy chats and session links), and pre-id transcripts
hydrate on load by recomputing the same hash — no migration state.
Names become display-only and may collide freely, which deletes the machinery
that defended them: the suffix-readback registration loop, session-row renaming
on rebuild (#freeNameForMessageRow, reserved-set #uniqueName), and the
reconciler's serialization guards (#syncSeq/#syncChain) — syncMessageScoped now
compares ids instead of awaiting blob text, so it is synchronous and cannot
interleave. A same-name clash within one draft gets a composer-local courtesy
rename before the id is minted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: resolve bare names to session rows and scope id searches to one row
A bare name is the roster's namespace: session links are advertised by
filename and have no other handle, so a same-named message attachment (which
is addressed by id) must not shadow them. get() now resolves session rows
first, keeping the message-row name lookup only as the fallback for
transcripts persisted before ids existed.
search_files restricted an id reference by mapping it back to the display
name and letting the worker filter on it — same-named files were then
searched together under one label. The tool now passes the resolved row
itself, so an id-scoped search can only ever hit its own file.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: label search hits with resolvable ids, normalize attach batches at commit
An unscoped search_files reported hits by display name only. Names may
collide, so a hit could not be mapped back to the row that produced it —
a follow-up read_file on the bare name could return a different same-named
file. Rows carrying an id are now labeled `name (file id: …)` in hit lines,
so every hit names the reference that resolves to exactly that row.
addTextFiles normalized (deduped, courtesy-renamed) each file against a
snapshot taken during its read loop. Attach batches overlap, so a file
committed by another batch between reads escaped both checks — duplicate or
same-named unsuffixed entries in one message. Normalization now runs in the
single synchronous commit step against the live list (foldIntoDraft), where
nothing can interleave.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: dedupe renamed re-drops in foldIntoDraft, truncate queued file chips
"Same file dropped twice" means same original (name, content), but a
courtesy rename erases the original name — an identical re-drop then missed
the duplicate check and landed as a further-suffixed copy. The dedupe now
also matches entries whose suffix-stripped base name equals the read's name.
Queued file chip labels get min-w-0 so long filenames truncate inside
max-w-36 instead of overflowing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: address cubic review — line counts, chip clicks, reference robustness
Five fixes from the cubic pass:
- The prompt advertised split('\n').length lines, one more than read_file
reports for newline-terminated files — textLineCount now matches the tool's
numbering (0 for empty, no phantom trailing line).
- Clicking a sent message's badge opened edit mode (the wrapper's
click-to-edit), unmounting the preview popover as it opened; the badge row
now keeps clicks to itself.
- resolve() accepts the composite label rosters and search hits print
(`name (file id: x)`) — models echo references verbatim, so the printed
form must resolve.
- fileToAttachedTextFile enforces MAX_TEXT_FILE_BYTES itself (raw size +
decoded byte length), so no ingestion path can persist an oversized
attachment past the composer's pre-check.
- Duplicate detection after a courtesy rename now uses an explicit sourceName
instead of inferring provenance from the display name — a user's real
`report (2).md` is not a rename of `report.md`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: exact names win over label parsing, commit recheck uses decoded bytes
resolve() parsed any `name (file id: x)`-shaped reference as a printed label,
so a session file literally named that way became unreachable by its exact
name (the dead-id fallback resolved the base name instead). Exact id and
exact-name lookups now run before label interpretation.
Attachment admission and the pending reservation use raw File.size, but the
committed charge is the decoded UTF-8 length — malformed input decodes each
invalid byte to a 3-byte replacement character, so a file passing the 8KB
text sniff could inflate past the conversation cap. The synchronous commit
step now re-checks the live budget against decoded sizes
(admitWithinByteBudget) and drops what no longer fits, with the budget toast.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: normalize files folded into the queued message
Repeated submissions during a stream aggregate into one queued message, but
their files were concatenated raw: an identical re-attach duplicated its chip
and ate a slot (possibly displacing a distinct file at the eight-file cap),
and a same-name clash skipped the courtesy rename. The queue now folds new
files through the same commit normalization as the composer — the queued
entry is a message draft like any other.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* style: compaction boundary label uses text-normal text-2xs
* fix: fold provenance survives pass-through, dequeued files fold into the draft
foldIntoDraft recorded sourceName only for renames it performed itself, so a
file already courtesy-renamed by the composer lost its provenance when folded
into the queue — a later re-attach of the original escaped dedupe. Folds now
compose: the original source name rides through every fold, and dedupe
matches on it.
dequeueMessage restored queued files into a possibly-populated composer by
raw concatenation; prependText now folds them like every other draft
aggregation (dedupe, courtesy rename) before applying the cap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: one MessageDraft owns the lanes that ship with a send
Review rounds kept finding the same P2 shape: an aggregation point where
files join a draft (composer commit, queue append, dequeue restore) that
forgot one of the draft rules — fold dedupe, courtesy rename, slot caps,
byte admission, lanes moving together. The rules existed only as convention
re-implemented per site.
MessageDraft owns them once: text, pastes, images, and text files live on
one object with addFiles (fold + optional decoded-byte admission + cap),
addImages (cap), prepend (restore-merge), replaceIfEmpty (occupied-guard
restore), and take (all lanes leave together). The composer holds a draft
instead of four state vars, and the queue is a draft behind the existing
queuedMessage/queuedImages/queuedFiles accessors — an aggregation point can
no longer skip a rule, because there is no raw array to concatenate into.
Deliberately not moved: @context and DOM picks (ContextManager owns their
lifecycle), the conversation byte budget's cross-composer ledger (store-side
follow-up), and sendRequest's options shape (it decomposes immediately and
is pinned by the manager test suite).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: correct the drop-routing comment, condense the budget doc
* fix: address cubic review — name sanitization, drop hold, merge restores
Four fixes from the cubic pass:
- Attachment display names render into model-facing prompt blocks, and OS
filenames may legally contain control characters — sanitizeAttachmentName
strips them at attach and again at every prompt-render site (legacy names
predate the attach-time pass), so a crafted name cannot inject prompt lines.
- Drop routing awaits handle/entry resolution before it can call
addTextFiles; a send during that window landed the dropped files on the
next message. The drop handler now holds sending (holdSendForIngestion,
taken before the first await) until routing completes.
- Restoring a taken queue after a failed auto-send replaced the queued draft
wholesale, silently losing a follow-up queued during the preflight. Both
#restoreQueue and the unmounted-input requeue now merge via draft.prepend —
the taken entry's text lands above the newer follow-up.
- restartGeneration validated the API restart index only after reserving the
resend bytes and truncating the transcript, so a stale index threw with the
reservation leaked and the display transcript half-mutated. The index is
resolved and validated before anything is touched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: restored drafts keep chronological priority, store names stay resolvable
A failed auto-send's restore folded the taken (older) draft's attachments
AFTER a follow-up queued during preflight, so at the slot caps the older
attachments silently dropped despite the text landing first — and only one
entry's pinned context survived. prepend() now puts the restored lanes ahead
(the cap drops the newest additions) and #restoreQueue unions both pinned
contexts by identity.
Session filenames were sanitized only at prompt render, so an id-less file
whose stored name carries control characters was advertised under a name
that resolve() could not match. Names are now sanitized at every store
row-creation site (attach, folder expansion, refresh, and persisted-row
restore for pre-sanitization records), making the advertised name the stored
name everywhere — render-site sanitization remains as defense in depth.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: update ee-repo-ref to aaa6cb89b05b76139252c64f057e53b94d12ac60
This commit updates the EE repository reference after PR #680 was merged in windmill-ee-private.
Previous ee-repo-ref: 4c08634af953db5c1125b1fb03f5af211fe21db3
New ee-repo-ref: aaa6cb89b05b76139252c64f057e53b94d12ac60
Automated by sync-ee-ref workflow.
* chore: repin ee-repo-ref to main's eb3690a34b (aaa6cb89 needs the unmerged AmqpTrigger OSS companion)
* Revert "chore: repin ee-repo-ref to main's eb3690a34b (aaa6cb89 needs the unmerged AmqpTrigger OSS companion)"
This reverts commit
|
||
|
|
dae7c49f21 |
test(git-sync): cover fork-of-dev-workspace branch naming and routing (#10231)
* test(git-sync): cover fork-of-dev-workspace branch naming and routing A throwaway fork of a dev workspace pushes to `wm-fork/<tracked>/<id>` (the tracked branch, not the dev's label), and the root's `sync_forks` poller enumerates `wm-fork/<tracked>/*` and routes commits on that branch into the nested fork through the root. This was twice assumed to instead live on `wm-fork/<dev-label>/<id>` and therefore never be collected/reconciled; these tests pin the real behavior. - CLI unit: `computeGitSyncDeployBranch` for a fork whose parent is a dev workspace resolves to `wm-fork/main/<id>`, explicitly not `wm-fork/dev/<id>`. - git-sync E2E: fork a dev workspace, assert the created branch is `wm-fork/main/<id>` (not `wm-fork/dev/*`), then assert a commit on it deploys into the fork via the root's sync_forks poller while the root is untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(git-sync): reconcile single-dev-per-root in fork-of-dev e2e The root workspace allows only one dev workspace, and a sibling test leaves one attached, so attach_dev_workspace failed with "already has a dev workspace". Detach any pre-existing dev before attaching, and detach ours via addCleanup so the test doesn't leak its own. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to aaa6cb89b05b76139252c64f057e53b94d12ac60 This commit updates the EE repository reference after PR #680 was merged in windmill-ee-private. Previous ee-repo-ref: 4c08634af953db5c1125b1fb03f5af211fe21db3 New ee-repo-ref: aaa6cb89b05b76139252c64f057e53b94d12ac60 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> |
||
|
|
7ac27c1ef2 |
fix(frontend): limit compare & deploy rows to the active direction (#10234)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4a898247a2 |
fix(apps): let entitled viewers read pre-existing S3 files from deployed apps (#10245)
* fix(apps): let entitled viewers read pre-existing S3 files from deployed apps Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): confine S3 viewer fallback to full unscoped sessions A scope-restricted token (e.g. apps:read:<app>, or an app-embed token) is allowed on apps_u/* but rejected by the route-scope middleware on job_helpers/*, so granting it the viewer fallback would be a new capability it cannot obtain directly. Gate the fallback on scopes.is_none() so only full sessions (which can already read via job_helpers) delegate; scoped and anonymous callers stay gated. Add a scoped-token isolation assertion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(apps): treat filter-tags-only tokens as unscoped for S3 viewer fallback The scopes.is_none() guard wrongly denied the viewer fallback to tokens that are effectively unscoped (empty scope arrays and if_jobs:filter_tags:-only tokens), which the route-scope middleware treats as unrestricted and which can therefore read the same file via job_helpers directly. Reuse that semantics via a shared is_effectively_unscoped helper so the relaxation covers exactly the tokens that gain no new capability, while genuinely scoped tokens stay gated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
555c751016 |
fix(db): repair s3 asset paths missing default-storage leading slash (#10243)
* fix(db): repair s3 asset paths missing default-storage leading slash Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01An2pTqSmqJd2XwnagvX4kM * fix(db): also repair script_trigger refs + exclude _default_ storage alias Extend the s3 leading-slash repair beyond the asset table: - script_trigger.trigger_ref (pipeline cascade edges, stored as s3://<path>) suffered the identical corruption: a window-era default-storage edge was recorded as s3://exports/x instead of s3:///exports/x, so it no longer matches the producer's post-fix write ref at dispatch (asset_dispatch does an exact trigger_ref = match with no DISTINCT), silently breaking the edge. Repaired with the same storage-name heuristic, with a dedup DELETE to avoid double-dispatch. - Exclude the reserved _default_ alias from the storage-name set. The runtime treats s3://_default_/key as the primary storage (fork_storage_ref), so _default_/key is a valid explicit-default ref; prepending a slash would corrupt it. Applies to both asset and script_trigger via the shared cache. join_pending_inputs (transient AND-join state) and materialized_asset_schema (ducklake-only) are intentionally left alone; documented inline. Verified end-to-end on a fresh DB: seed prior-state rows as the pre-fix parser would have persisted them for data pipelines and scripts, run the full migration suite, assert every row matches the fixed-parser identity (default repair, hive/root/nested keys, named-storage + _default_ untouched, pre-cutoff untouched, non-s3 untouched, duplicate collapse) across both tables. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
7fb8a2e390 |
fix(parsers): keep s3 asset path suffix verbatim to preserve storage distinction (#10241)
* fix(parsers): keep s3 asset path suffix verbatim to preserve storage distinction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01An2pTqSmqJd2XwnagvX4kM * package json * fix(pipelines): preserve named storage in generated TS/Python S3 URIs The TS/Python templates emitted `s3:///${s3Key(path)}`, stripping the leading slash and pinning the URI to default storage. For a named-storage asset path (`secondary/key`) that produced `s3:///secondary/key`, which resolves to the default storage with key `secondary/key`, dropping the named-storage dependency and reading/writing the wrong object. Emit the path verbatim after `s3://` (matching the DuckDB template) so a named-storage input/output keeps its storage; identical to the previous output for default-storage paths. Removes the now-unused `s3Key` helper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(cli): align bun.lock parser versions with frontend The PR bumped windmill-parser-wasm-asset (1.749.0→1.753.0) and windmill-parser-wasm-regex (1.692.0→1.764.0) in package.json and the npm package-lock.json for both cli and frontend, but cli/bun.lock was left pinned to the old versions. Sync it so the CLI's wasm asset parser (used by localGraph inference) matches the frontend and deploy-time parser. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
dc5b006e7f | fix npm checks | ||
|
|
2caee41fdf |
fix(worker): mount /dev/shm as tmpfs in the Docker v2 nsjail sandbox (#10240)
The Docker v2 nsjail profile provided /dev/null, /dev/zero, /dev/random, and /dev/urandom but omitted /dev/shm, since generate_rootfs_mounts() skips the image's own /dev in favor of the profile's device nodes. Any program needing POSIX shared memory (Ansible/Python multiprocessing, Chromium) failed with "No such file or directory: /dev/shm". Add a /dev/shm tmpfs mount, matching run.ansible.config.proto and run.python3.config.proto. Fixes WIN-2216 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
68debab877 |
feat(triggers): add AMQP (RabbitMQ) trigger via lapin (#10230)
* feat(triggers): add AMQP (RabbitMQ) trigger using the lapin library Fixes WIN-2214 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(triggers): defer AMQP cross-workspace deploy pending utils-internal publish Revert the amqp_trigger additions to the shared windmill-utils-internal TriggerDeployKind and the frontend cross-workspace deploy adapter: the frontend installs the published npm package, which lacks the new kind until a release is cut. AMQP create/edit/delete/list/sync/capture are unaffected (they use local types); only cross-workspace deploy/merge of AMQP triggers waits on the package bump. Also document the at-most-once ack in the consumer loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): address AMQP review — at-least-once ack, workspace cascade, contracts - ack AMQP deliveries only after successful dispatch; nack+requeue on failure - add ON DELETE CASCADE workspace FK so amqp_trigger rows are cleaned on workspace deletion (and the listener stops) - fix the /amqp_triggers/test OpenAPI body and add amqp_trigger to WorkspaceDiffRow.kind - register AMQP in the generated workspace trigger tool (create_trigger) - drop banned $bindable defaults on optional props in the config section - add build_uri unit tests (encoding, ports, vhost) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): stop AMQP poison-message loop and reconnect on transient drops Chaos testing against a live RabbitMQ broker showed the previous nack(requeue) + immediate re-poll spun a tight redelivery loop (~1000 critical-error reports/sec) on a poison message, and any connection blip permanently disabled the trigger (lapin has no built-in reconnect). - on dispatch failure: nack+requeue then stop consuming; the listener framework re-lists the trigger after its ping goes stale (~15s), backing redelivery off to that cadence instead of a tight loop (verified: rate dropped from ~1000/s to ~1 per ~26s, message preserved) - on connection/stream error: stop and let the framework reconnect instead of disabling; persistent failures are still disabled via get_consumer (verified: a forced connection close now auto-reconnects and resumes) - finish the AI create-trigger action wiring for AMQP: add amqp to CreatedResourceTriggerKind, the action-card registry, and the drawer registry so the result card renders and its "Open" action works Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): complete AMQP frontend registries and defer merge rows - add amqp to capturableTriggerTypes (so AmqpCapture mounts), the Runs jobTriggerKinds filter, and CLOUD_DISABLED_TRIGGER_TYPES - wire AMQP into global AI chat mode: TRIGGER_KINDS, the request union, writeTriggerSchema, triggerServices, and the draft adapter - stop emitting actionable AMQP fork-comparison rows (revert amqp_trigger from TRIGGER_OR_SCHEDULE_TABLES) since cross-workspace deploy is deferred until windmill-utils-internal is published — avoids a deploy that fails with "Unknown kind: amqp_trigger" - use design-system TextInput instead of raw <input> in the config section Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): complete AMQP session/draft registries and constrain prefetch - add amqp to the session-deploy, draft-compare, preview-router, and copilot workspace-item registries so AMQP drafts/deploys/nav/path resolution work - include amqp_count in the MoveDrawer attached-trigger rename warning - replace the raw prefetch <input> with a design-system TextInput bounded to an integer 1-65535 (backend u16) and block save on invalid values Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): make AMQP disconnect/reconnect consistent with the Kafka trigger lapin, like rdkafka, has no transparent reconnect, so the AMQP listener now mirrors the Kafka trigger's explicit reconnect loop instead of relying on the framework re-list (which disabled the trigger once get_consumer failed on a sustained outage): - get_consumer returns cheaply; consume owns a (re)connect loop that retries with a 30s backoff, reports a critical error every 10 failed attempts, and reports a recovered critical error once it reconnects — never disabling the trigger on a connectivity failure - a consumer/stream error breaks out to reconnect rather than disabling - dispatch failure still nacks+requeues (at-least-once) with a short backoff to avoid a tight poison-message loop, keeping the connection alive Verified against a live RabbitMQ broker: killing the broker keeps the trigger enabled and retrying (attempt N), and restarting it auto-reconnects (logs "reconnected after N attempts") and resumes dispatch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): complete AMQP capture registries and constrain prefetch contract - add the 'amqp' case to triggerKindToTriggerType so opening the AMQP editor from a capture button no longer throws "Unknown TriggerKind: amqp" - register AmqpIcon in CaptureTable's icon map and add an AMQP entry to the script/flow CaptureButton menu - bound the OpenAPI prefetch_count to an integer 1-65535 (matches the Rust u16) and regenerate clients/prompts - require a non-empty exchange name when the exchange binding is enabled - build_uri: fall back to "/" on a blank vhost and bracket IPv6 hosts (+ tests) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(triggers): wire AMQP into pipeline graph, git-sync, and preprocessor types - asset_graph: discover attached amqp_trigger rows and emit an AMQP TriggerEdge so AMQP triggers render (and can be opened/deleted) on the data-pipeline canvas - frontend pipeline graph: add amqp to NativeTriggerKind, the add-trigger menu, node presentation, event-trigger set, annotation keywords, and the editor/service registrations - git-sync: add the amqp_trigger include pattern (+ test) so an AMQP git-sync deployment stages only its .amqp_trigger.* file, not an unrelated same-path object - preprocessor starters: add the AMQP event to the generated TS/Python/PHP trigger event types (kind/payload/exchange/routing_key/queue_name/redelivered/ delivery_tag) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): finish AMQP pipeline/parser wiring, prefetch validation, source lists - fix a stray edit that corrupted the pre-existing MqttTriggerEditor import ($lib/... path) in PipelineTriggerEditors.svelte - reject prefetch_count = 0 server-side in validate_config (RabbitMQ treats 0 as unlimited) and defensively skip basic_qos(0) in build_consumer (covers the capture path that bypasses CRUD validation) - recognize `// on amqp` in the canonical parser (TriggerSpec::Amqp) and add amqp to the CLI non-autorun/event-trigger sets so a pipeline cascade never runs an AMQP-only node as a manual root without an event - add amqp to the preprocessor intro lists and both pipeline AI instructions Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(triggers): reject zero AMQP prefetch in all paths and finish guidance lists - extract a shared validate_amqp_options used by both CRUD validate_config and build_consumer, so capture configs (which bypass CRUD validation) also reject prefetch 0 instead of silently connecting with an unlimited buffer (+ unit tests for 0/1/65535/None) - add AMQP to the main script-writing preprocessor-sources prompt and the CLI triggers-skill guidance list Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(triggers): de-duplicate AMQP prefetch comment and fix GET response text - keep the zero-prefetch rationale only on the shared validate_amqp_options doc; drop the redundant call-site comments - correct the getAmqpTrigger OpenAPI 200 description ("deleted" -> "retrieved") Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to aaa6cb89b05b76139252c64f057e53b94d12ac60 This commit updates the EE repository reference after PR #680 was merged in windmill-ee-private. Previous ee-repo-ref: 5da5fd65aca9594b2611837a52e4677b544b0380 New ee-repo-ref: aaa6cb89b05b76139252c64f057e53b94d12ac60 Automated by sync-ee-ref workflow. * chore(migrations): consolidate the four AMQP migrations into one The table and the three enum ADD VALUE statements (trigger_kind, job_trigger_kind, draft_kind) are one atomic feature. ALTER TYPE ... ADD VALUE runs inside the migration transaction on PG >= 14 (Windmill's minimum) since the amqp_trigger table doesn't reference those enum types, so they can share a single migration instead of four. Verified applying cleanly in a single transaction on a fresh DB. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- 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> |
||
|
|
c7ee192680 | update ee ref to git sync cli prompt fix (#10224) | ||
|
|
c6fd6da971 |
repoint slack hubPaths to form-data 4.0.6 versions (#10239)
The image prebundles the hub scripts listed in hubPaths.json by their pinned (immutable) version. windmill-integrations republished the four image-cached slack scripts with form-data bumped 4.0.5 -> 4.0.6; point hubPaths.json at the new versions so freshly built images cache the fixed lock instead of the stale one. Fixes part of #10219 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d24e176816 |
fix(parser): spurious pg arg inferred from placeholders in comments (#10226)
* fix(parser): ignore pg placeholders in comments, strings and dollar quotes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NXWRDKzswMeDBP5THXpb9Q * package json + lock * chore: remove stray root npm lockfiles Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kuUhCPD9ph4ZuFd13SCWJ --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b0bf25683b |
fix(frontend): prevent browser back-swipe navigation over monaco editors (#10229)
* fix(frontend): prevent browser back-swipe navigation over monaco editors Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EEP1T4UogYABBifdNJUUuQ * fix: toggle root overscroll-behavior-x on editor hover instead of cancelling wheel events Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EEP1T4UogYABBifdNJUUuQ * refactor: replace per-editor swipe guard action with single global handler in root layout Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EEP1T4UogYABBifdNJUUuQ * style: compress swipe-guard comment to four lines Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q97WNWUT9mntEEVhg6kpzv --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
32994df427 |
fix: return to parent workspace when a fork is deleted remotely (#9898)
* fix: return to parent workspace when a fork is deleted remotely When a workspace fork was deleted remotely while a user had it open, reloading stranded them: a regular member got logged out (whoami fails on the vanished workspace) and a superadmin silently landed on a dead workspace whose requests 404. Detect the deleted fork on load and redirect to its parent (or the workspace picker) with a toast instead. - forkParentMemory.ts: persist a bounded fork->parent map in localStorage while a fork is reachable (the parent is unrecoverable post-deletion). - (logged) layout: record the current fork's parent via an effect. - root layout: tryRecoverFromDeletedFork detects the vanished fork in loadUser and redirects to the remembered parent or the workspace picker, reusing the workspace list already fetched on mount. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: verify fork existence for superadmins before deleted-fork redirect * fix: only recover deleted fork on actual 404 from superadmin check * fix: recover prefixless dev-workspace forks via remembered parent * fix: use workspace exists check to detect deleted forks * fix: restore wm-fork- detection for non-member superadmin forks * fix: record fork parent for non-member superadmin dev workspaces * Update frontend/src/routes/(root)/+layout.svelte Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * fix: store fork parent map with null-prototype to handle __proto__ ids --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> |
||
|
|
9bc1f62128 |
feat(ai): session chat nits — empty sends, command picker polish, session-state prompt (#10233)
* feat(ai): session chat nits: empty sends, picker polish, session state * fix(ai): scope empty-send turns to global chat and pin new behavior * fix(ai): align grouped search nav with display order, enable empty-send button * fix(ai): fork fallback for unlisted workspaces, section headers across branches * style(ai): hint-colored 3xs picker section headers, drop inline row descriptions * style(ai): more spacing between picker sections * fix(ai): require context elements for empty global-chat sends * style(ai): no empty bubble for text-free messages * fix(ai): review round 2 — keyboard tooltip access, requestedMode guard, no display names in prompt * fix(ai): queue context-only drafts pressed while a response streams * fix(ai): shared context identity for queued badges and full queue-context union |
||
|
|
39b9a9d3d5 |
bump slim image Python to 3.12.13 to ship current pip (#10236)
* fix(docker): upgrade bundled pip in uv-managed Python runtimes uv-managed Python runtimes ship a pip that lags behind, and it gets baked into the full, slim, and EE slim images at build time. Add a pinned PIP_VERSION build arg and upgrade pip in every real managed runtime after `uv python install`, failing the build if any upgrade fails. Fixes part of #10219 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docker): simplify pip upgrade to native uv per-version install Replace the runtime-discovery loop with a single `uv pip install --python <ver> --system --break-system-packages --upgrade` per known Python version. uv resolves the managed interpreter by version, so no directory scan or symlink handling is needed; a plain RUN still fails the build if the pinned pip can't be installed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docker): bump slim image Python patch to 3.12.13 for current pip The full image floats LATEST_STABLE_PY to 3.12 (→ 3.12.13), whose uv/python-build-standalone runtime already ships pip 26.1.2. The slim and EE-slim images pinned 3.12.12, which ships pip 26.0.1 — the only runtime actually behind. Align them to 3.12.13 so they inherit the current pip natively, instead of adding an explicit pip-upgrade build step. This supersedes the earlier pip-upgrade approach on this branch: the full image needed no change, and the slim gap was purely the stale patch pin. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |