mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-15 08:02:53 +00:00
datatable-external-instance
15007
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9b182aaf38 |
fix: surface workspace ids on duplicate names and explain fork promotion (#10291)
* fix: disambiguate same-named workspaces in the workspace menu Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: explain why git promotion is absent on a fork Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: link a fork to dev-workspace pairing from git sync settings Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
bf16e7d49a |
feat: surface workspace-script advanced settings in flow editor (#10289)
* feat(flow-editor): surface workspace-script advanced settings in flows
Workspace-script steps in a flow could not view or edit script-level
runtime settings (concurrency, cache, timeout, debounce, dedicated
worker, priority, delete-after-use). The concurrency and cache tabs
only showed a "set it on the script" warning with no value and no way
to act on it.
- Add ScriptAdvancedSettings, a reusable subset of the script editor's
runtime settings, and two entry points that reuse it:
- WorkspaceScriptSettingsDrawer: a mini settings drawer reachable from
the flow step (header "Settings" button and the delegating tabs),
saving a new script version with the code left unchanged.
- an inner "Settings" drawer inside ScriptEditorDrawer, saved together
with the code.
- Replace the concurrency/cache delegation warnings with a box that
fetches the referenced script's current value and offers an
"Edit script settings" shortcut (useWorkspaceScriptSettings loader).
- Add ScriptSettingsBadges showing active advanced settings, in the
standalone script editor top bar, the edit-code drawer, and above the
workspace-script step preview.
Fixes WIN-2233
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(flow-editor): keep subflow concurrency note distinct from workspace-script
The concurrency delegation box is workspace-script specific; subflow
steps now keep a plain limitation note instead of the script settings
shortcut.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(flow-editor): preserve all script fields when saving settings-only version
Building the createScript body by hand dropped codebase/labels/envs and
other fields on the new version. Spread the loaded script instead and
override only lineage, matching ScriptEditorDrawer's save.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(flow-editor): address review — settings-save safety and stale display
- WorkspaceScriptSettingsDrawer: keep settings-only saves from hijacking
execution identity or discarding the author's draft (preserve_on_behalf_of
+ skip_draft_deletion), and normalize cleared concurrency/debounce keys to
undefined so blanks don't become shared global keys.
- ScriptEditorDrawer: normalize cleared keys in its save too (the inner
settings drawer edits them).
- FlowModuleComponent: reload the surfaced concurrency/cache values + badges
after a header settings/code save; gate settings editing on customUi.scriptEdit.
- useWorkspaceScriptSettings: sequence-guard load() against stale overwrites.
- Add unit tests for getActiveScriptSettingsBadges.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(flow-editor): round-3 review — concurrency-safe save, load guards, UI gates
- WorkspaceScriptSettingsDrawer: drop auto_parent so a settings-only save uses
the loaded parent as an optimistic-concurrency guard (fails loudly instead of
silently reverting a concurrent deploy); sequence-guard openDrawer so a slow
load for a previous script can't clobber a reopened one.
- useWorkspaceScriptSettings: clear loading in the superseded/early-return path
so a hub/empty step can't spin forever.
- ScriptBuilder: gate the clickable settings badges on customUi.topBar.settings
and settingsPanel.disableRuntime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(flow-editor): round-4 review — template, load-error, legacy-zero handling
- WorkspaceScriptSettingsDrawer: stop forcing is_template=false so saving a
setting on a template keeps its template status; show a recoverable error
(with Retry) when the settings load fails instead of spinning forever.
- scriptSettings/FlowModuleComponent: treat non-positive concurrent_limit and
timeout as unset (legacy zero rows), so no "Max 0 executions"/"Timeout 0s".
- Add badge tests for the non-positive cases.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(flow-editor): round-5 nits — neutral card wording, load-error surfacing, cache zero
- WorkspaceScriptSettingInfo: neutral "managed on the referenced workspace
script" header (no longer claims "configured" when unset) and a distinct
error line so a failed load isn't misread as "not set".
- useWorkspaceScriptSettings: expose an error state; thread it into the
concurrency and cache cards.
- Treat cache_ttl <= 0 as unset, matching concurrency/timeout; add test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(flow-editor): icon-only script action buttons + gate settings in local-dev
- Gate the workspace-script settings actions (header button, clickable badges,
Concurrency/Cache shortcuts) on the settings drawer actually being mounted, so
the local-dev flow editors (Dev.svelte / flows/dev) that provide the context
store but never render the drawer keep the values read-only instead of showing
no-op controls.
- Make the script action buttons icon-only with clear hover popovers to save
space in the crowded step/script-editor top bars: Edit, Settings and Fork in
the step header, Settings in the edit-code drawer, and the settings badges
(icon chip + label/value popover).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(flow-editor): round-6 nits — a11y names + accurate read-only reason
- Add aria-label to the icon-only Edit/Settings/Fork buttons and the setting
badges so keyboard/screen-reader users get an accessible name (the hover
popover alone didn't expose it).
- WorkspaceScriptSettingInfo takes a noEditReason so the read-only explanation
matches the actual gate (hub / hash-pinned / unavailable-in-this-editor)
instead of always blaming hub/pinned — fixes the wrong reason shown in the
local-dev flow editors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(flow-editor): drop narrating comment on the no-edit-reason derived
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(flow-editor): bind settings save completion to the drawer target
The drawer is a singleton, so a save that outlived a reopen ran the new
target's callback and closed its drawer, discarding edits in progress.
Capture the target sequence and callback at save time: the captured
callback still fires (it refreshes the script it belongs to) while the
close, error toast and saving flag only apply if the target is unchanged.
Reopening also resets the saving flag, which the seq-guarded save no
longer clears for a superseded target.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
a29e13fd18 |
reference the file-search worker by its packaged .js name (#10290)
svelte-package does not rewrite the string literal inside new URL(), and ships only the compiled searchWorker.js — so the .ts URL is dangling for any downstream consumer of @windmill-labs/components (rollup: Could not resolve searchWorker.ts). Vite maps .js back to the .ts source in-repo, so both builds resolve. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f02df7fc45 |
feat(monitor): make between-steps zombie flows hand-recoverable (#10287)
* feat(monitor): make between-steps zombie flows hand-recoverable When a worker is OOM-killed mid state-transition, the flow is reaped as a between-steps zombie (children all success, module still InProgress). We do not auto-recover (a re-driven transition can OOM again), so instead: - Append actionable recovery guidance to the cancellation reason when the reaped step's state is derivable (every child a success completion): which step, iterations completed, raise memory then restart-from-step (UI + API). - Restart-from-step now reuses a zombie step verbatim (InProgress with all children successful) and restarts from the next step, so no completed child re-runs; downstream steps re-derive its result from flow_jobs on demand. - Cast flow_status ::text in the reaper query: reading the jsonb column as Box<str> included the binary version byte and silently failed FlowStatus parsing (disabling the restart-not-yet-started branch since the v2 migration). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): only reuse a between-steps zombie step that provably finished Address review findings on the zombie-restart reuse path: - Require structural completeness (FlowStatusModule::is_between_steps_complete): a serial for-loop / branch-all reaped mid-fan-out has an all-success prefix but unrun remaining iterations, so the cursor must sit on the last element; while-loops are never derivable (continuation is a post-iteration condition). Parallel containers preallocate all children, so success alone is conclusive. Shared by the monitor guidance and the restart resolution. - Decline reuse when the step carries stop_after_if / stop_after_all_iters_if: those predicates decide whether downstream steps run, and reuse would bypass them; such a step re-runs instead. - Decline reuse when the zombie step is the last module (advancing past it lands on the failure step); it falls back to the existing re-run path. - Unit tests for is_between_steps_complete and an integration test asserting a mid-iteration serial-loop zombie is re-run, not reused. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): align zombie recovery guidance with restart eligibility Address CI review findings: - Exclude skip_if / suspend / sleep (not just stop predicates) from reuse via FlowModule::allows_zombie_reuse, so a skipped/suspend-armed step is never synthesized as Success (which would strand a restart waiting on an approval it never armed). - The reaper does not load the flow definition, so it cannot know whether restart will reuse or re-run a given step; reword the guidance to state both outcomes (reuse where derivable, re-run for the flow's last step or one carrying a stop/skip condition, approval, or sleep) instead of promising "no re-run". - Make the mid-iteration regression test exercise the cursor-completeness guard: a downstream step makes the loop non-final, so reuse is prevented only by the guard; a truncated loop result would then fail the assertion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): never let zombie reuse swallow a nested restart request A nested restart (RestartedFrom.nested) descends into the restart step's child to re-run an inner step. For an eligible zombie BranchOne/Subflow the outer branch_or_iteration_n is None, so reuse fired, skipped the container, and the explicitly requested inner step never re-ran. Thread the presence of a nested chain into restarted_flows_resolution and decline reuse when set. Regression test added (RED without the guard: the nested target is reused instead of re-run). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): don't auto-requeue preprocessor zombies as unstarted flows The ::text parse fix re-activated the "hasn't started yet, restart it" branch, but its `modules[0] == WaitingForPriorSteps` check also matches a flow whose preprocessor is still InProgress (step == -1, first module waiting). Requeuing such a flow re-runs the preprocessor, duplicating side effects / repeating the OOM. Gate the branch on FlowStatus::is_not_yet_started, which also requires the preprocessor (if any) to be WaitingForPriorSteps. Unit-tested. Also drop the numbered procedural narration from the happy-path test comments. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): only emit restart guidance for restartable (deployed, top-level) flows The recovery guidance points operators at the run page's "Re-start from" button and the restart API, but both require a top-level deployed flow: a preview has no flow path (the button is hidden, the API 400s) and a subflow child restarts via its root, not itself. Gate the guidance on runnable_path IS NOT NULL AND parent_job IS NULL so previews/subflows keep the existing wording instead of being told to use a button/endpoint that isn't there. Verified end-to-end: a reaped preview gets no RECOVERY block, a reaped deployed flow does. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): gate recovery guidance on kind='flow' to match the restart surface Addresses review nit: a pathful editor preview (kind='flowpreview' with a runnable_path) satisfied the previous runnable_path check but the run page only renders the "Re-start from" button for kind='flow'. Match that condition exactly so previews/singlestepflow keep the plain wording. Verified end-to-end: a reaped pathful preview now gets no RECOVERY block. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): disable zombie reuse for raw-flow (editor preview) restarts A JobPayload::RawFlow restart queues the request's current, possibly EDITED, definition, but restarted_flows_resolution validates reuse against the completed job's STORED definition. For an eligible preview zombie, editing the restart step and restarting from it would synthesize Success from the old children and skip the edit. Thread allow_zombie_reuse into the resolver (true only for JobPayload::RestartedFlow, which queues the stored definition) and decline reuse for raw-flow restarts. Regression test added (RED without the guard: the edited step is skipped and the old result is reused). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(sqlx): add offline cache for zombie_flow_recovery test queries The integration test's UPDATE v2_job_completed queries had no .sqlx entry, so the CI SQLX_OFFLINE build of the test failed to compile. Regenerated with --all-targets --features deno_core,quickjs to capture the test-target queries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(monitor): drop procedural narration from the raw-flow zombie test Per AGENTS.md (comments record constraints, not narration): remove the two step-describing comments the reviewer flagged; the test doc comment already carries the durable rationale. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): restrict zombie reuse to monitor-reaped flows The reuse predicate matched the InProgress/all-children-success shape without checking provenance, so an ordinary force-cancel at the same boundary (a child succeeded before its parent transition landed) would also be reused, dropping the usual restart-from-step re-run. Gate reuse on canceled_by = 'monitor' (the username the zombie reaper cancels with). Regression test added (RED without the guard: a user-cancelled flow reuses the child instead of re-running it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(monitor): reuse zombie step on Some(0) too, so the run-page button works The run page's "Re-start from" button always sends branch_or_iteration_n = 0 (never omits it), but reuse only fired for None, so the exact UI path the recovery message points to would re-run the children instead of reusing them. Treat a whole-step restart (None or Some(0)) as reuse-eligible; Some(n>=1) keeps the explicit partial-container restart. Verified against the live EE restart API with branch_or_iteration_n=0: all loop-iteration child UUIDs are reused. Happy- path test now sends Some(0) to match the button. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8eb36ce008 |
fix: treat concurrent_limit/timeout <= 0 as unset instead of a zero cap (#10288)
* fix: treat concurrent_limit/timeout <= 0 as unset instead of a zero cap Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: flow-step timeout <= 0 inherits the script timeout, not the global default Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
68daed8501 |
refactor: custom-instance datatable connection handling (#10271)
Attach custom-instance datatables in the DuckDB executor through a DuckDB secret instead of an inline connection string, and route postgres triggers on custom-instance datatables through a dedicated custom_instance_replication_user role (with its own auto-generated password in global_settings). Normalize custom_instance_user attributes on server boot. Claude-Session: https://claude.ai/code/session_01Tp6NNNinCB8dwWqGaFXDRF Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
65e504146d |
feat: data-pipeline recorder, interactive player, and deploy-to-hub recording (WIN-2156) (#10055)
* feat(frontend): add data-pipeline run recorder and interactive player Adds a recorder/player for data pipelines, mirroring the existing flow and script recorders. Arm "Record" on a pipeline, run it, and the resulting cascade is captured into a downloadable JSON that the /replay player can rerun fully offline. Because a pipeline run is a cascade of independent jobs (not a single root SSE job like flows), the recording captures three things: the resolved asset graph, the per-node cascade status timeline (from the orchestrator's onUpdate), and each node's job stream (opened via getupdate_sse on launch). The player renders the graph read-only, animates the recorded node transitions in real time, and lets you click any node to inspect its recorded args, logs and result — reusing the same JobLoader replay path the flow/script players use (setActiveReplay + isReplay gating), so no network calls are made during replay. - recording/types.ts: PipelineRecording, PipelineTimelineFrame, RecordedNodeState - recording/pipelineRecording.svelte.ts: createPipelineRecording() store - recording/PipelineRecordingReplay.svelte: the player component - replay/+page.svelte: dispatch type === 'pipeline' - pipeline/[folder]/+page.svelte: Record toggle + Download recording; capture the whole-pipeline / bounded cascade run Fixes WIN-2156 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): capture DuckLake/datatable data samples in pipeline recordings Follow-up to the pipeline recorder/player: asset nodes are now inspectable offline in the player, showing what each table held after the recorded run. At record finalization, for each ducklake/datatable asset in the pipeline the recorder samples the table (up to 100 rows + columns + row count) reusing the exact live-preview query path (loadAllTablesMetaData + getRows), so a replayed sample matches what the asset-detail pane would have shown. Captures are best-effort and per-asset — a missing/unconfigured table is stored as an error marker, never thrown, so the recording still completes. The player renders the sample as a read-only typed grid when an asset node is clicked (script nodes keep their logs/result/args detail). - recording/types.ts: PipelineAssetSample + assetSamples on PipelineRecording - recording/pipelineAssetSample.ts: capturePipelineAssetSample() helper - recording/pipelineRecording.svelte.ts: recordAssetSample() + assetSamples - recording/PipelineRecordingReplay.svelte: asset-node data-sample panel - pipeline/[folder]/+page.svelte: sample each asset in finalizePipelineRecording Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * recorder * feat(hub): record data pipelines in deploy-to-hub with interactive player Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(hub): match editor cascade timeout, warn on cycles, reset badge on re-run Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(recording): address review — finalize race, stale replay timers, /replay redirect, bounded sampling, jobs validation Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(recording): structural recording validation, guard-clear + SSE cleanup on throw paths Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(recording): validate nested graph arrays and timeline frame statuses Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(hub): scope recording to bundle membership, fail cyclic runs, validate recording elements Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(hub): prune recorded graph + asset samples to bundle membership Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(recording): validate graph.triggers array and per-job initial_job/events shapes Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(recording): guard non-object payloads, event elements, and asset-sample/code maps Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(recording): render error boundary + validate trigger_kind and non-empty sample error Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(recording): validate event.data and recorded-job shapes for all replay types Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(recording): make the replay event timer crash-proof against malformed events Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(recording): await replay completion and boundary-wrap all three players Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(recording): guard flow Play handler, cap ?src= download size, trim comment Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
+4 |
30eedf9ee1 |
feat: Add section to deploy projects to hub (#9332)
* feat: add Deploy to Hub workspace settings tab * Init record logic * Fix wordings * Add publish-app drawer with per-app rate limit mock - Publish drawer on raw_apps/apps exposes public URL, copy-iframe, unpublish - Inline per-app rate limit config (req/min, burst, per-IP toggle) - Rename workspace settings "Default app" tab header to "Apps" to cover both default app and public rate limiting Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Simplify publish drawer to show workspace-wide rate limit only Drop per-app rate limit fields (req/min, burst, per-IP) — none of these are supported by the backend. The drawer now shows the existing workspace-level rate limit read-only with a link to edit it in Workspace settings → Apps. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Rename publish-app drawer wording to 'Share as iframe' 'Publish publicly' was ambiguous (publish to Hub vs make public URL). Use 'Share as iframe' for the button and drawer title, and 'Generate iframe' for the confirm action. Intro text now explicitly mentions iframe embedding use cases (Hub, docs page, own site). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Wire DeployToHub to real workspace data - Fetch apps, raw_apps, flows, scripts, resources via their services - Fetch workspace rate limit via WorkspaceService.getSettings - Share-as-iframe flips app policy.execution_mode to 'anonymous' via AppService.updateApp and resolves the real public URL via getPublicSecretOfApp + computeSecretUrl - Detect already-public apps from listApps execution_mode field - Filter out app_theme resources (noise, present in every workspace) - Hub bundle/version push and recording remain mocked (no backend yet) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Wire recordings to real jobs with run-preview UX - Recording flow now fetches the real schema, runs the job, and polls getCompletedJobResultMaybe to surface success/failure before saving. - Drawer shows a sticky status box (loader / success / failure) with a result preview, a job link, and an in-context Save CTA. - Only successful runs can be saved as a recording. Failures show the error and offer re-run. - Filter cache/state/app_theme internal resource types (mirrors workspaces_export.rs filter). - Added "What is a recording?" explainer banner above the items list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add draft/review state machine and submission gating - Phases: predeploy → draft → under_review → live, with workflow step indicator and contextual footer actions per phase - Bundle drawer collects name + readme before pushing the draft - draftItems snapshot frozen at deploy time; workspaceItems keep refreshing without affecting the draft - Folder MultiSelect lets users scope the bundle to one or more folders; empty = whole workspace - Submit-for-review disabled until every script and flow in the draft has a recording (progress bar + counter) - Recordings now run the real job and poll for success/failure; only successful runs can be saved - under_review phase locks editing, sharing, and recording - Dark mode variants on every coloured banner - Steps card shows the full 3-step process always, highlighting the current step Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Make recordings optional, encourage them for discoverability - Submit for review no longer gated on full recordings - Footer hint now frames recordings as boosting approval speed and public Hub featuring, not as a hard requirement - Progress card label switched from 'Recordings needed' to 'Recordings recommended' - Items without a recording display a yellow 'No recording' badge in every phase so the gap stays visible after submission Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Allow per-item selection inside the bundle scope - Items in predeploy now have checkboxes (all selected by default) - Select all / Deselect all act on the current folder filter - manualDeselected resets when the folder filter changes - Bundle button uses the selected count, disabled when zero - Draft snapshot keeps only the selected items - Checkboxes hidden in draft / under_review / live phases Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add diff button once approved by admins * Small fix * Nits * fix(deploy-to-hub): paginate workspace list and cancel stale record polls - loadWorkspace fetches all pages instead of capping at 100 items per kind - pollJobUntilComplete now bails when recordRunSeq advances (new record target, re-run, or drawer close), preventing late completion of a previous run from overwriting current state Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * perf(deploy-to-hub): parallelize public-app URL resolution resolvePublicUrl now runs once per anonymous app via Promise.all instead of serially inside the items loop, removing N round-trips from initial tab load. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(indexer): tell admins when ingress routes search to wrong pod (#9274) * [ee] fix(indexer): tell admins when ingress routes search to wrong pod When the IndexReader is absent on the pod handling a search request but another pod is actively holding the indexer lock, the EE handler now returns a tailored error pointing at the ingress/load-balancer configuration instead of the generic "indexer not running" message. The indexer status endpoint reads the DB lock so it reports "running" from any pod, but search endpoints need the in-memory IndexReader that only exists on the lock holder. In multi-replica deployments this looks like the indexer is healthy but every search 404s. Companion: windmill-labs/windmill-ee-private#TBD Fixes WIN-1968. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to eb18d7b4c0e37fea3f6e1e2cc44e0fddd74ff817 This commit updates the EE repository reference after PR #586 was merged in windmill-ee-private. Previous ee-repo-ref: 7dd43d1850813071cc18ba49ba090583e7321f4b New ee-repo-ref: eb18d7b4c0e37fea3f6e1e2cc44e0fddd74ff817 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> * feat(cli): add `wmill init prompts` and custom override slot (#9266) * feat(cli): add `wmill init prompts` and custom override slot Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(cli): replace init prompts with refresh prompts + AGENTS.md/AGENTS.cli.md split Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(cli): dedupe claude skills via @-includes and add prompts freshness check Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(cli): drop migration-choice flags from `refresh prompts` Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(cli): add 'Running and previewing local changes' section to AGENTS.cli.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): write full skill content to .claude/, drop @-include wrapper Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(cli): reconcile CLAUDE.md the same way as AGENTS.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): address PR review nits — argv parsing, lazy import, comment detection, error propagation Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: add yolo mode for ai chat tools (#9258) * feat: add yolo mode for ai chat tools * nit * fix: align chat footer controls * feat: add ai chat autonomy modes * feat: add autonomy mode dropdown * fix: highlight yolo autonomy icon * fix: auto accept flow edits * fix: hide unsupported autonomy modes * fix: handle auto-accept flow editor races * fix(debugger): add non-root user support to Dockerfile (#9277) Mirrors the main Windmill Dockerfile pattern: creates a windmill user (UID/GID 1000) and makes cache/work directories world-writable so the image runs cleanly under Kubernetes securityContext.runAsNonRoot or runAsUser: 1000 without permission errors on Bun, pip, or windmill cache writes. Fixes WIN-1969 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ai): enforce RLS and scope check on user-supplied X-Resource-Path (#9276) * fix(ai): enforce RLS and scope check on user-supplied X-Resource-Path The AI proxy handler accepts an X-Resource-Path header to override the configured workspace AI provider. When supplied, the handler loaded the resource value from the resource table using the root DB pool with no resources:read scope check, so any authenticated workspace user could point X-Resource-Path at a restricted AI resource (e.g. one in a folder they cannot read) and the proxy would use that resource's provider credentials for the outbound AI request. For user-supplied resource paths, now require resources:read:{path} scope and fetch the resource through user_db.begin(&authed) so RLS enforces the same folder/group boundary as the resource API. The RLS- scoped $var: resolution stays in place as defense in depth. The admin-configured workspace/instance ai_config path is unchanged. Fixes WIN-1971 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(ai): regression test for X-Resource-Path RLS enforcement Cover all four cases: - non-admin pointing X-Resource-Path at a restricted resource is rejected - non-admin pointing it at a resource they own still works - admin can point it at any resource - workspace-configured proxy flow (no X-Resource-Path) is unchanged Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: add userdraft listing primitives (#9268) * feat: add userdraft listing primitives * fix: cancel stale userdraft discard writes * docs: remove global ai userdraft plan * feat(nsjail): optional disk-backed /tmp via instance setting (#9272) * feat(nsjail): optional disk-backed /tmp via instance setting * test(nsjail): unit-test tmp mount resolver and narrow visibility * refactor(nsjail): switch tmp backing to select + conditional UI * ui(nsjail): make tmpfs the visible default in /tmp backing select * fix(nsjail): refuse preexisting jail_tmp to block symlink escape * fix(nsjail): allow jail_tmp reuse on sequential nsjail calls Codex flagged that python/ruby/rust executors invoke nsjail twice per job_dir (install then run). The previous resolver treated any preexisting jail_tmp as hostile and silently fell back to tmpfs on the second call, so disk-backed mode never reached the main script run for those langs. Use symlink_metadata().is_dir() to distinguish a real directory left by an earlier call in the same job_dir (safe to reuse) from a symlink or other entity (still refused, as the codebase-tar escape requires). Also loosen the frontend visibility predicate: only hide nsjail settings when job_isolation is explicitly 'none' or 'unshare', so deployments that enable nsjail via DISABLE_NSJAIL=false with no DB setting can still see the controls. * chore(main): release 1.706.0 (#9270) * chore(main): release 1.706.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> * fix(nsjail): gate unix-symlink test behind cfg(unix) for Windows build (#9280) The disk_backed_refuses_preexisting_symlink_at_jail_tmp test calls std::os::unix::fs::symlink directly, which doesn't exist on Windows targets. Without a cfg gate, `cargo check --tests` fails on Windows with E0433. Other symlink call sites in this crate (php_executor, bun_executor, rust_executor, etc.) already follow this pattern. Fixes WIN-1972 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Reduce slim image vulnerability surface (#9279) * Reduce slim image vulnerability surface * chore(docker): drop apt-get upgrade -y from slim images apt-get upgrade hurts build reproducibility (same Dockerfile + same commit at different times produces divergent images) and trips hadolint DL3005. The freshness it buys is dominated by simply rebuilding against the periodically-refreshed debian:bookworm-slim base image. The --no-install-recommends and apt-list cleanup wins are kept. --------- Co-authored-by: Ruben Fiszel <ruben@windmill.dev> * fix(git-sync): bump to hub/28234 with stateless gpg.program wrapper (WIN-1974) (#9282) * fix(git-sync): revert LATEST_GIT_SYNC_SCRIPT_PATH to hub/28230 to restore GPG-signed deploys (WIN-1974) hub/28231 (PR #9230) is the "thin" script that hands the actual `git commit` to the CLI's hidden `sync git-deploy`. The hub script still does the GPG setup (import key into a fresh GNUPGHOME, dummy `gpg -bsau` to warm the agent passphrase cache, then `git config user.signingkey` + `commit.gpgsign` locally), but the commit no longer runs in the same `git_push` flow — it runs minutes later inside the CLI after workspace API resolution, zip pull, file extraction, and lockfile autofill. By the time the spawned `git commit` asks gpg-agent for the cached passphrase, the cache state is no longer reliable (or the spawned `gpg` ends up talking to a fresh agent), so signing fails non-interactively with `gpg failed to sign the data`. hub/28230 is hub/28217's in-script logic rebuilt with windmill-cli@1.703.3: the GPG setup and the in-script `sh_run("git commit ...")` happen back-to-back in `git_push`, so the cache is always fresh. It preserves wm_deploy / fork branch behavior, the EE deployment-callback `main()` signature is unchanged, and the only min-version check in EE (`is_script_meets_min_version(28103)`) is comfortably below 28230 — so this revert is safe. Forward fix (separate PR): publish a new thin script that, alongside the existing GPG setup, writes a `gpg.program` wrapper using `--pinentry-mode loopback --passphrase-file` so signing is independent of the agent's cache state. Re-bump past 28231 then. Fixes WIN-1974 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(git-sync): check in source-of-truth for the next hub script (gpg.program wrapper) This is the script that will be published to hub.windmill.dev once verified on a customer GPG-signed deploy. It replaces hub/28231's agent-cache pre-warm (`gpg -bsau` with --passphrase) with a stateless gpg.program wrapper + chmod-600 passphrase file. Every git-invoked gpg call goes through the wrapper, which always uses --pinentry-mode loopback (and --passphrase-file when a passphrase exists). Signing no longer depends on gpg-agent having a cached passphrase by the time the CLI's `git commit` runs — which closes WIN-1974. Not wired in yet: LATEST_GIT_SYNC_SCRIPT_PATH stays on hub/28230 until this script is uploaded and the new hub id is known. This file is checked in so the diff is reviewable, future bumps have a source of truth, and a CLI regression test can `cat` it for fixture parity. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(frontend): skip format/pattern validation for $var/$res/$jsonvar references in ArgInput A resource field with a `pattern` constraint (e.g. the gpg_key.private_key field, whose pattern enforces a `-----BEGIN PGP PRIVATE KEY BLOCK-----` prefix) rejects values like `$var:u/me/gpg-private-key` with an "invalid format" error in the resource editor — even though `$var:`/`$res:`/`$jsonvar:` are placeholders the backend resolves at runtime, not the actual string that needs to match the regex. Bail out of all format/pattern checks (email, ipv4, ipv6, uuid, custom pattern) when the value is one of these references. Required/numeric bounds/array checks still apply since they're shape-level, not regex. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(git-sync): bump LATEST_GIT_SYNC_SCRIPT_PATH to hub/28234 (gpg.program-wrapper fix) hub/28234 is the forward fix for WIN-1974: replaces hub/28231's agent-cache pre-warm (which became stale by the time the CLI's `git commit` ran) with a stateless `gpg.program` wrapper that uses `--pinentry-mode loopback` (and `--passphrase-file` when a passphrase exists) on every gpg invocation. Bundled CLI is windmill-cli@1.705.0. Verified via reproducer at /tmp/git-sync-diff/test-gpg-fix.sh: deliberately killing gpg-agent between GPG setup and `git commit` reproduces the customer's `gpg failed to sign the data` error verbatim under the old flow, and the wrapper signs through it. Holds for passphrase-protected keys, split-subkey [C]+[S] layouts, and unprotected keys. Drops the local source-of-truth copy (`hub-scripts/`) — hub is canonical now that 28234 is published. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(git-sync): drop verbose comment above LATEST_GIT_SYNC_SCRIPT_PATH The git history (this PR) carries the why; the constant name + value carry the what. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(cli): wmill sync git-deploy stops committing; caller owns commit+push (#9284) Single contract for the deployment-callback path: the CLI does branch checkout + pull, the caller (hub script in production, test in test) does git add + commit + push. This restores the WIN-1974 invariant — GPG setup and `git commit` run back-to-back in the same process, so the agent's pre-warmed passphrase cache is still warm at sign time — without needing a `--skip-commit` flag for the hub case and a default "also-commit" for everything else. Same behavior in every call site. Changes: - sync.ts: drop the gitSyncDeployPush call from pull()'s deploy path (both the onlyCreateBranch fast-return and the post-pull commit). `gitSyncDeployPush` stays exported for any caller that wants the same commit/push semantics — just not invoked by the CLI subcommand. - gitsync_promotion.test.ts: e2e test now does its own git add + commit + push after `wmill sync git-deploy`, mirroring what the hub script does in production. Same regression coverage (wm_deploy branch created in Case A, main untouched; main updated in Case B, no new wm_deploy). CLI typecheck unchanged (two pre-existing TarAsZip errors at lines 2578/3307, present before this PR). All 743 unit tests still pass. The accompanying hub script (option-C — CLI for branch+pull, script for commit+push) lives at /tmp/git-sync-diff/sync-script-to-git-repo-windmill.option-C.ts. Once published, a follow-up bumps LATEST_GIT_SYNC_SCRIPT_PATH to its id. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * bump git sync to 28236 * fix: fork compare visibility for non-admins and stale-token superadmins (#9283) * fix: use fork-scoped authed for fork visibility in compare_workspaces * test: add EE end-to-end repro for fork rename visibility * chore: restore concurrency_locks sqlx cache lost in cleanup * test: add regression for stale-superadmin-token fork visibility bug * chore: update sqlx cache for new test queries * chore(main): release 1.706.1 (#9281) * chore(main): release 1.706.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> * feat: add wmill job rerun subcommand (#9275) * feat: add wmill job rerun subcommand * feat: add wmill job restart subcommand for flow restart-at-step * chore(system_prompts): point plugin skills sync at plugins/windmill/ (#9287) * chore(system_prompts): point plugin skills sync at plugins/windmill/ The plugin checkout's plugin folder is being renamed from `plugins/windmill-code-plugin/` to `plugins/windmill/` to shorten the slash-command namespace and align with the matching Cursor plugin layout. Paired with windmill-labs/windmill-claude-plugin#8. That PR must merge first so the next sync run finds the new folder. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(system_prompts): update plugin-dir example to plugins/windmill Co-authored-by: centdix <centdix@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: centdix <centdix@users.noreply.github.com> * fix(cli): wmill sync pull updates wmill-lock.yaml for raw apps (#9289) * fix: flow recording teardown crash + rename package to @windmill-labs/components (#9288) * fix: guard against null recording during FlowRecordingReplay teardown Navigating away from a flow recording inside a workspace file-tree view threw `TypeError: Cannot read properties of null (reading 'flow')` from FlowGraphViewer once during the teardown tick. Svelte 5 compiles child component props as live getters that close over `$$props.recording.flow`. When `recording` flips to null on the parent's navigation, an outer `{#if !recording?.flow}` doesn't stop those getters from firing one more time as derived effects re-evaluate before the unmount lands — so the getter dereferences null and throws. Fix at the two layers where the deref actually happens: - FlowRecordingReplay: use `recording?.flow` at the binding sites (FlowViewer + graph-snippet FlowGraphViewer) so the compiler emits an optional-chained getter, and guard the snippet branch with `{:else if recording?.flow}` so it doesn't mount when there's nothing to show. - FlowGraphViewer: finish the optional chaining the rest of the file already used everywhere else (`flow?.value?.skip_expr`, `flow?.value?.cache_ttl`, `flow?.schema`). When the upstream binding returns undefined during teardown, the graph degrades to an empty frame instead of crashing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: rename package to @windmill-labs/components - frontend/package.json: rename `windmill-components` → `@windmill-labs/components` - frontend/publish.sh: drop the in-place sed rename dance; the checked-in name now matches what's published, so `npm run package && npm publish` is enough - frontend/package-lock.json, system_prompts/auto-generated/prompts.d.ts: regenerated by `npm run package` under the new name Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(flows): restore Variables and Resources in flow editor prop picker (#9290) The design system overhaul in |
||
|
|
fa3644281f |
fix(monitor): diagnose zombie-flow OOM on the transition worker, not q.worker (#10286)
handle_zombie_flows() joined worker_ping on the flow's queue-row worker
(q.worker) only. In nested/subflow/forloop cases that worker is frequently
NOT the one that performed the flow's final state transition, so all OOM
detection ran against a healthy bystander and the diagnostic wrongly told
on-call to chase a deadlock / SIGQUIT.
Change 1: when q.worker itself looks healthy, additionally search worker_ping
for a plausible culprit — a different worker on the same pod (worker_instance)
or worker group whose last ping clusters around the flow's last_ping and has
since gone silent (the signature of a worker OOM-killed mid state-transition),
picking the one closest in time to the transition. If found, the message names
that worker and routes to the OOM explanation. The extra query runs only on the
rare zombie path and fails soft (warn + fall back) so it never blocks the cancel.
Change 2: reorder the healthy-q.worker hint to lead with "check whether a
different worker on the same pod/group was OOM-killed around {last_ping}",
keeping deadlock/SIGQUIT as the secondary possibility.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
1f912f4104 |
feat(ai): enable data pipelines in AI sessions with alpha notice (#10273)
* feat(ai): enable data pipelines in AI sessions with alpha notice * test(ai): assert real pipeline instructions returned in session * fix(ai): await pipeline editor registration in open_preview to end build_pipeline_node race * test(ai): drop historical narration in pipeline instruction assertion * docs(ai): clarify S3 asset-URI storage forms and literal-key rule in pipeline prompt * docs(ai): document data_upload S3Object input mechanism in pipeline prompt * test(ai): guard open_preview pipeline tool-registration wait seam * feat(ai): report inferred asset lineage in build/edit_pipeline_node results * test(ai): assert open_preview(pipeline) awaits async handler registration * docs(ai): qualify S3Object as wmill.S3Object in pipeline prompt examples * fix(ai): report failure when a backgrounded session's pipeline tools never register * fix(ai): include output_kind-seeded outputs in build_pipeline_node lineage feedback * fix(ai): report only body-detected lineage, never the output_kind seed placeholder * fix(ai): include materialize target in lineage feedback, condense comments * docs(ai): use default-storage s3:/// in canonical pipeline read example |
||
|
|
771eb284ad |
chore(pr-skill): make review-round waiter idempotency-aware (#10285)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
248c8751b1 |
feat(ai-chat): let the session/global chat create email triggers (#10282)
* feat(ai-chat): let AI create email triggers for flows and scripts The AI chat could not identify that it can create email triggers. The `open_page` tool advertised `email` as a trigger kind (routing to /email_triggers), but neither `write_trigger` (global/session chat) nor `create_trigger` (flow chat) accepted `kind: "email"` — their enums stopped at 10 kinds. Seeing email as a navigable page but having no tool to create one, the model concluded email triggers were unsupported and faked them with an HTTP webhook plus an external mail provider. Add `email` to the shared trigger-schema generation (`WORKSPACE_TOOL_*` in system_prompts/generate.py), which feeds both `triggerRequestSchemas` (global) and `createTriggerToolSchema` (flow), then wire the kind through the global chat draft path (TRIGGER_KINDS, draft-kind map, triggerServices/labels, write_trigger config union) and the flow chat `triggerConfigs`. The backend route, EmailTriggerService, `trigger_email` draft kind, and email UI drawers already existed. Add discovery eval guards (`flow-test17`, `global-test29`) that assert the model selects `kind: email` for an implicit "run when an email is received" request. They assert the recorded tool-call kind rather than the resulting draft, so they hold on a CE backend where email trigger routes (smtp+private-gated) are not compiled. A mocked unit test in global/core.test.ts covers the full draft->read->deploy path. Verified: flow guard 5/5, global guard 4/4 (sonnet); frontend unit tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): default email trigger workspaced_local_part before persist The global chat forwards the write_trigger draft unchanged, but workspaced_local_part maps to a NOT NULL column and the model may omit the optional field — deploying such a draft would fail instead of creating the trigger. Default it to false at draft-write time (as flow chat does at create), and assert the deployed request carries it in the unit test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): make email trigger listing and edits robust Two issues from adding the email kind to the global chat: - list_workspace_items iterated every trigger kind and awaited each list unconditionally; email routes 404 without smtp+private (as do EE kinds on CE), so one unavailable kind threw and dropped the entire trigger listing. Skip a kind whose list endpoint is unavailable. - Defaulting workspaced_local_part on the incoming config before the base merge reset an existing trigger's value: editing a workspaced trigger while omitting the optional field overwrote true with false, changing its receiving address. Default on the merged draft instead, so an omitted field keeps the existing value and only genuinely new drafts get false. Adds regression tests for both. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): only skip 404 trigger-list failures, surface the rest The prior fix caught every rejection from a per-kind trigger list, so an auth, 5xx, or network failure produced a successful-but-incomplete listing that could make the chat treat existing triggers as absent. Skip only a 404 (route not compiled in) and re-throw anything else. Adds a propagation test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
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> |