mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-03 16:02:12 +00:00
main
1977
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d4a423f567 |
fix: bound the pg cached-connection reset probe so a vanished server fails fast (#11505)
Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
013b302d48 |
perf: keep a pg executor connection per database (#11492)
* fix: stop the pg executor cache from pinning a pooler slot Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: retry while the evicted pg backend exits Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * perf: keep a pg executor connection per database Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi * fix: close a pg connection whose query was cut off by a timeout or cancel Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi * fix: keep a pg connection only after its query read every result Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi * fix: free idle pg connections and retry when a connect is refused Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi * fix: retry a pg connect only when a connection cap refused it Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi * fix: detect pg connection caps by SQLSTATE and label cloud connections as new Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
e7fc1b2e2e |
feat: restricted job tokens per script and flow (#11484)
* feat: restricted job tokens (job_token_scopes on scripts and flows) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: admit flow-run reads, skip dedicated workers, gate on worker version Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep restricted jobs off flow runners, preserve scopes on rename and promotion Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep restricted jobs off every dedicated handoff, confine progress flow id Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: exclude restricted runnables from dedicated worker startup Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: gate restrictions on the release after 1.821.0 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: store per-job scopes on job_perms instead of v2_job, pin inline runs to the checked version Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: step-level job_token_scopes for flow steps and agent tools Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: fail closed on perms read errors, refuse restricted queue imports, gate step scopes in previews Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: carry a job's scopes on its completion so a re-run keeps the caller's cap Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: carry a zombie job's scopes into its completion Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: leave a zombie for the next sweep when its scopes cannot be read Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * docs: correct the QueuedJobV2 completion comment Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: validate step scopes in batch flows, fail closed on unvalidated step scopes Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: refuse flows with step or tool restrictions at push while an older worker is live Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: apply the step-scope worker gate to flow restarts Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: list the job token toggle with the other step and flow settings Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * chore: pin the EE companion merged with EE main Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * perf: skip scope lookups for unrestricted jobs; list job token setting last Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * style: rustfmt scopes tests Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: confine restricted job tokens to their own run lineage; drop remaining extra lookups Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * chore: update ee-repo-ref to 259ad3bfeef5285ba80eedc86309b11dca001220 This commit updates the EE repository reference after PR #843 was merged in windmill-ee-private. Previous ee-repo-ref: 2b77c0225dca441235daf7bf0a06ba968df0c927 New ee-repo-ref: 259ad3bfeef5285ba80eedc86309b11dca001220 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
b7f74cd562 |
fix: never reuse a pg connection a script left inside a transaction (#11497)
* fix: never reuse a pg connection a script left inside a transaction Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi * fix: close a pg connection whose job failed instead of caching it Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi * docs: describe every caller of PgConnectionLease::discard Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
d7227726be |
feat: log pg job progress, warn on stalls, detect dead db connections (#11491)
* feat: log pg job progress, warn on stalls, detect dead db connections Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi * fix: log only slow pg steps to keep job log writes off fast statements Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi * fix: say when a slow connect included a failed cached-connection reset Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHjj2iG8kKPCaFuK6bqXvi --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
eed7e7d9e6 |
fix: stop the pg executor cache from pinning a pooler slot (#11490)
* fix: stop the pg executor cache from pinning a pooler slot Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: retry while the evicted pg backend exits Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
14801fdd68 |
fix: unstick postgres jobs on large results with custom-typed columns (#11475)
* fix: unstick postgres jobs on large results with custom-typed columns Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: carry the parked-rows delivery fix in the postgres fork Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: describe postgres queries before streaming instead of parking rows Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * perf: skip the postgres describe for statements that return no rows Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: look for returning in the whole postgres statement Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: read the leading postgres keyword past nested block comments Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: end leading postgres line comments at CR too Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
ae093b7666 |
fix: read inet and cidr results with their prefix, including arrays (#11476)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
c25bb1ed90 |
feat: add //no_network annotation for native scripts (#11445)
* feat: add //no_network annotation to deny all network access in native scripts Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: close no_network bypasses (quic dns, header parsing, token, non-native) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: refuse //no_network on deno regardless of //native, derive disabled ops from deno_net Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor: scope //no_network to native scripts only, drop bun/deno refusal Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
94ac34a489 |
fix: await async def main in python scripts (#11437)
* fix: await async main and preprocessor in python scripts * fix: reuse one event loop per python process for async main * fix: reuse the event loop a python script sets at import * docs: clarify python await helper comment |
||
|
|
cec8eaeadb |
test: fix CLI and Windows cargo test failures on 1.820.0 (#11429)
* test(cli): stop CLI tests resolving a just-published windmill-client Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: quote the Windows cert path in the pg attach assertion Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(cli): note when the windmill-client pin must move Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
039f49ef12 |
feat: dispatch ai agent tools through the worker queue (#11227)
* feat: dispatch ai agent tools through the worker queue * fix: respect worker tags when executing ai agent tools locally * fix: cancel only direct-child tool jobs on agent failure * fix: cancel queued descendant tool jobs on agent failure * fix: route ai agent tools by the flow's tag rule |
||
|
|
c5f59d6ccc |
fix: drop caller-supplied _MODULES at push for every job (#11418)
* fix: drop caller-supplied _MODULES at push for every job Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: never interpolate _MODULES into a tag and drop unused mut Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
d1260d7c5a |
feat: refuse OIDC tokens to previews that impersonate a path their user cannot write (#11396)
* feat: add provenance claims to job OIDC tokens and mark preview sub Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: require a flow or script job's version to belong to its path for deployed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: derive app script paths server-side and test job provenance in CE Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: count an app script as deployed only when a deployed app run stamped it Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: state the deployed condition for the preview sub prefix Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: keep the plain OIDC sub for previews by users who can write the path Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: refuse OIDC tokens to previews by users who cannot write the path Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor: keep OIDC token issuance unchanged, leaving provenance to the claims Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor: name the root job's trigger claim root_trigger_kind Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: report the request-supplied paths in a job's provenance Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: count a worker-pushed preview under deployed code as deployed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: drop the claimed parent of a restarted flow preview Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: only read a job's modules from its args when it is a preview Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: never count a preview whose modules came in its args as deployed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to dba91044a174f8c9275fefea2619dc966b0f9250 This commit updates the EE repository reference after PR #832 was merged in windmill-ee-private. Previous ee-repo-ref: a703cb17776d0858255fbfd817dce132377d3e80 New ee-repo-ref: dba91044a174f8c9275fefea2619dc966b0f9250 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
c1b59f70dd |
feat(ai-agent): add compaction memory that summarizes older context (#10928)
* feat(ai-agent): add autocompacted memory that summarizes older context Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): compact on final-answer turns and count what a turn appended Address the pre-push review findings on the compaction path: - A turn the model answers without a tool call left the agent loop on its first iteration, so a chat-shaped step never compacted and reloaded the whole conversation on every later turn. Compaction now also runs after the loop. - The trigger measured only the last request, so a single large tool result could carry the next one past the window without ever crossing 80%. - The summarization call re-sent the usage-tracking request shape on endpoints the loop had already learned to drop it for. - The flat 8000-token summary reserve swallowed the whole target on a small context window, leaving one message in the tail and summarizing the rest. - A response cut off inside the <analysis> scratchpad was accepted as a summary. - The chat-mode memory default was a shared object the step form edited in place. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): keep Anthropic prompt counts and compact once per response Address the first CI review round on the compaction path: - Anthropic's streaming parser dropped `message_start`, the only event carrying the prompt-side counts, so a native Anthropic run reported no input tokens at all and compaction fell back to a character estimate. - A loop that exits without issuing another request — a structured-output turn does — reached the post-loop pass still holding the previous measurement and compacted a second time, or retried a failure with nothing changed. - The summarization call inherited the step's `max_completion_tokens`; a low one truncates the summary inside its scratchpad, which counts as a failure and disables compaction after three of them. - A fired trigger that found nothing to summarize said nothing. - Memory already over the window — a lowered `context_window`, or a step moved over from `auto` — had no way back, since compaction only ran after an accepted request. It now also runs once before the first one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): state the summary's own completion cap and drop the pre-flight pass - The summarization call asked for no completion cap at all, which is "uncapped" only on the OpenAI-shaped providers: Anthropic substitutes 64000, over several Claude models' output ceiling, and Bedrock leaves the model's own small default, short enough to cut the response off inside its scratchpad. It now asks for the reserve the split already set aside, raised to the step's cap when that is larger. - Compaction no longer runs before the first request. The fallbacks the loop learns from a rejection are not known that early, so on exactly the endpoints that need them the summarization was malformed by construction: it failed, spent a strike, and the first agent request still carried the oversized conversation. A memory already past the window is repaired on the turn after a request the endpoint accepts, rather than by a pass that cannot succeed there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): ask the summary for exactly the room the split reserved The split scales its reserve down on a small window while the request asked for a flat 8000, so the two diverged below an 80k window: on a 4k/8k model the cap alone exceeded the window and every summarization was refused, and on a 20k one a full-length summary could land the conversation back over the trigger and compact its own previous summary on the next response. Both now read one `summary_reserve_tokens`. The call also no longer inherits the step's reasoning effort. Every provider counts thinking against that same budget, so a high-effort model could spend the whole reserve before writing anything and return a summary cut off inside its scratchpad; the compaction prompt asks for an `<analysis>` block, which is the reasoning this call needs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): charge the compaction budget for tools and the system prompt The tail budget was the whole target, but a request also carries the system prompt compaction keeps and the tool definitions, which are not in the message list at all. On a small window those are most of it: a tail sized to the full target left the next request back over the trigger, compacting again every response, and the no-usage estimate missed the tool schemas entirely so it could fail to trigger at all. Both now account for them. The reserve also gains a floor. It is the summary's output cap as well as the room the split leaves, and scaled down without one a small window gave a structured nine-section summary a few hundred tokens — truncated inside its scratchpad every time, which is discarded, which switches the mode off after three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): count Gemini's tool-use prompt tokens in an agent step's usage Gemini splits a tool-using turn's input across `promptTokenCount` and a disjoint `toolUsePromptTokenCount`, and its thinking apart from `candidatesTokenCount`. The agent step's parser read only the headline fields, so every tool-using turn under-reported both — and the compaction trigger, which runs off the reported prompt, could not see the tool results that grew it. It now goes through the same helpers the proxy path already used. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): calibrate the compaction estimate against the measured prompt Two rounds running, the finding was "the character estimate cannot see input X" — tool schemas, then S3 attachments, which are short paths in the message list and whole images by the time a provider counts them. Enumerating those is a list that only grows, so the estimate is now scaled to the one number that is ground truth: what the provider charged for the last request. Attachments, tokenizer drift and whatever comes next fall out of that, because the estimate is only ever used relative to itself. Also stop the Gemini helpers turning an absent count into `Some(0)`. Downstream, absent means "fall back to estimating the conversation" while zero reads as an empty prompt and would hold the trigger below its threshold for the whole run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): charge attachments what they cost and let a heavy short prefix compact The calibration conserved the conversation's total cost but spread it by character count, so an attachment — a short S3 path in the message list, a whole image or PDF once a provider expands it — was charged to the text messages around it and stayed nearly free in the split. It now carries a nominal cost of its own, which the calibration corrects a residual on rather than the whole gap. The four-message minimum also refused exactly the case that fix is for: an attachment arriving on the first or second turn can pass the trigger before four removable messages exist, and summarizing even one of them saves most of the prompt. A prefix worth a quarter of the window is now enough on its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): never summarize a prefix holding only a previous summary The message-count floor was carrying a second job: a fresh summary sits in a one or two message prefix, so requiring four declined it. The share threshold added last commit admits it, and a summary is reserve-sized by construction — so the post-compaction shape could spend one summarization per response swapping a summary for another the same size, shrinking nothing and losing fidelity each time. A previous summary no longer counts towards that threshold. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): take the context window from the model and drop the estimate calibration Brings compaction in line with how the AI session does the same job, which had already answered these three questions. - The window is looked up from the model. `MODEL_CONTEXT_WINDOWS` in `windmill-ai/src/model_context.rs` mirrors the session's table in `copilot/modelConfig.ts`, entry for entry and with the same matching rules; each side points at the other, since a model added to one and not the other compacts at two different sizes. A step's `context_window` becomes the override for what the lookup cannot serve, and chat mode writes none. - Provider usage is normalized where the provider's quirk is, not at the consumer. `TokenUsage::with_cache_beside_input` raises `input_tokens` to the whole prompt for Anthropic and Bedrock, which report their cached prefix beside it; the OpenAI shape already counts it inside. `prompt_tokens()` is then just `input_tokens`, rather than inferring the shape from whether a write count is present. - The estimator is no longer calibrated against the measured prompt. The session uses the provider's count when it has one and a chars/4 estimate otherwise, with nothing in between, and a tail sized a little wrong only compacts again a turn later. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * feat(ai-agent): summarize memory down to what the database can store Without an instance object store, memory is a 100KB database row cut from its oldest message, the summary included, so compaction on a mainstream model never got to keep anything across runs. A step that persists there now runs its post-loop compaction pass against the smaller of the model's window and the cap at chars/4, about 25k tokens: the loop keeps the whole window, and what is written is a summary plus a tail that fits. The run logs when that pass summarizes, and how many messages the write dropped when one still overshoots. The editor's storage warning on the option is removed: nothing exposes the instance storage to it, so it keyed on the workspace S3 setting, which is unrelated to where memory goes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): get a complete, billed summary out of every provider Compaction against the real providers turned up four things the stub could not: Gemini and OpenAI's reasoning models think by default and bill it against the same cap the summary must fit in, so the summarization request now asks them for their least (none, low); an OpenAI Responses call that hits max_output_tokens ends in response.incomplete, whose usage the parser dropped, so that summarization went unbilled; a summary that quotes </summary> when it describes its own instruction was cut off at the quote, on the agent step and the AI session alike; and the prefix could end on an unanswered user message, after which the instruction reads as part of that turn (Anthropic merges the two outright). The tail now starts on a user message, and both prompts tell the model the instruction is not part of the conversation. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): compact down to half the window, on the agent step and the AI session The gap between the 80% trigger and the target is what one compaction buys, and every summarization request carries most of the window. At a 70% target a 128k model summarized about 13k tokens of prefix for a summary of up to 8k, so each ~100k-token request bought a few turns of room before the next one re-summarized the previous summary. At 50% the same request frees about 30k. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): drop the workspace-S3 memory hint and state the database bound in the tooltip The memory field warned that memory is kept in the database whenever the workspace had no S3 storage. That setting has no bearing on where memory goes: the instance object store decides, and nothing exposes it to the editor. The field's tooltip now describes both memory kinds and states the database bound unconditionally; the run log says what happened. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): send the summarizer its tool history as text The summarization request carries no tool definitions, and Bedrock's Converse API rejects toolUse/toolResult blocks that arrive without them, so on Bedrock every summarization of a prefix holding a tool call failed silently until the breaker tripped. The prefix's tool calls and results now reach the summarizer rendered as text, on the agent step and in the AI session's compaction, which goes through the same proxy. Also drops the TokenUsage::prompt_tokens accessor, which had become a plain read of the normalized input_tokens, and shortens the context window field's description. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): price attachments from the provider count, bound storage in bytes, effort per pro model Addresses two Codex rounds and a leftovers audit. - Attachments were priced at a flat 1500 tokens in the split, so a multi-page PDF (tens of thousands of tokens to the provider, a short S3 path in the message list) could be kept in the tail or leave no prefix worth summarizing. They are now priced from the provider's count for the request that carried them, less that request's text, with the 1500 floor where nothing was counted. - The database storage bound measured the provider's token count, but the 100KB cap is bytes and repetitive text packs several characters per token. The persist pass now measures the serialized conversation. - The summarizer forced `low` on every reasoning model, which the pro variants reject (gpt-5-pro takes only high, gpt-5.2-pro starts at medium); they now get no effort. - Dropped the unused prompt_tokens accessor and its orphaned assert, an unused PartialEq, a needlessly public lookup, and fully-qualified Gemini calls; refreshed stale comments and the memory_id schema doc; regenerated the flow schema artifacts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): evict a heavy attachment into the summarized prefix, not the tail Pricing attachments from the provider count was not enough on its own: a leading attachment is a user message, and the boundary rule pulled the last unanswered user turn back into the kept tail to keep it with its answer. For a heavy attachment that dragged it into the tail — or, at the front, emptied the prefix — so it was never summarized and rode every request. The boundary now moves forward instead, keeping that user turn and its answer in the summarized prefix. Verified on the running instance: a 25k-token PDF on a 30k window is summarized out on the turn it overflows, and later turns drop from 26k to ~1.5k tokens. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): keep the forward boundary move off tool results and the prefix start The forward move that keeps an unanswered user turn out of the tail had two edges the third Codex round found: advancing past the user could land the boundary on a tool result (its tool_calls then summarized away, orphaning it), and with no system prompt the summarizable prefix starts at 0, so a trigger firing while the tail estimate fit everything indexed below the start and panicked the task. The forward scan now skips tool-opening boundaries, and the move is guarded above the prefix start. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): drop the step temperature from the summary request OpenAI's reasoning models (gpt-5-mini, gpt-5.1, gpt-5.2) reject `temperature` alongside any reasoning effort but their own default, so a step configured with a temperature made every summarization fail once the summarizer forced a low effort — history then grew unchecked. The internal summary call now omits the step's temperature: a structured extraction does not need a set one, and omitting it sidesteps each provider's temperature-versus-reasoning rules. Confirmed against the API that low + temperature is refused on those models. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): compact an oversized loaded memory before the first request Compaction was reactive, taken only after a request the endpoint accepted, so the fallbacks the loop learns from a rejection are known first. But a memory loaded from an earlier run can already exceed this run's window — the step was switched to a smaller model, or a run under a wider one persisted more than fits — and that first request then overflows and fails the run, with every retry reloading the same history and failing again. A pass is now taken up front, off the character estimate, before the first request. It uses the default request shape; an endpoint needing a fallback may reject this one summary, which is non-fatal, and mainstream providers need none. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): under the storage bound, trigger on the max of bytes and model tokens The storage-bound pass measured only the serialized row size, so an attachment — a few bytes as an S3 path but nearly the whole model context — read as tiny and the pass skipped a compaction the model needed. It now takes the larger of the byte measure and the model's token count, since repetitive text is few tokens but many bytes and an attachment is the reverse; either being over must fire a pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): drop oldest turns when a summary cannot fit the window, as the AI session does An oversized loaded memory (a step switched to a smaller model, or an object-store run that persisted more than a later model's window holds) left a prefix larger than the summarizer's own window, so the summary request overflowed and failed, the memory was untouched, and every retry failed the same way. The AI session handles this by falling back from summarization to dropping the oldest turns down to the target; compaction here now does the same. When a summary cannot run — it failed, the breaker is tripped, or nothing is worth folding — the oldest turns are dropped until the conversation fits and opens on a user message, keeping the newest turn. The next request then always fits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): drop whole turns only, keep the storage pass to bytes, refresh the count after a rewrite Three edges the seventh Codex round found, all in the drop-oldest fallback and the storage-bound measure: - drop_oldest_to_fit dropped to any point that freed enough, which could strand a tool result whose tool_calls went with the messages before it. It now drops whole turns only, always landing the boundary on a user message and never splitting the newest turn; a lone turn too big for the window is left whole rather than broken. - The storage-bound pass measured the whole model prompt against the shrunk 25k window, so a large tool roster and the system prompt — neither written to the row — tripped it on a conversation the row easily held. It measures the serialized bytes alone now; the model's own window is enforced by the in-loop passes and the pre-first-request pass, so the persisted size is all this pass is for. - A compaction rewrites the message list, so the provider's count for the request that produced it no longer lines up. The count is now cleared after any pass that rewrites the conversation, so a later pass measures the estimate over the actual messages instead of a stale, larger prompt (which could decline a summary that already fit and then drop it). The step temperature, no longer sent to the summarizer on any path, is dropped from the request struct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): measure only the persisted messages against the storage cap Persistence strips the system prompt before writing the memory row, but the storage pass was serializing every message including it, so a large system prompt with a tiny conversation reported far over the storage trigger, and the fallback dropped the one real turn, run after run. The storage measure now serializes only the non-system messages, matching what the row actually holds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix(ai-agent): run the model-window pass before the storage-bytes pass post-loop A turn the model answered without a tool call broke before the in-loop compaction check, so on database-backed memory its only pass was the storage one, which measures bytes. An attachment fills the model context but is a few bytes in the row, so that turn never compacted and a follow-up could overflow the model. The post-loop now runs a model-window pass first, off the provider's count, then the storage-bytes pass when the row is smaller than the model — both limits enforced for a chat-shaped step, not just the one that happens to bind. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix: simplify agent compaction and preserve execution history * fix: remove unused compaction history setting * fix: preserve answers and recover rejected agent context * refactor: make agent compaction transactional * fix: skip agent summaries that cannot fit retained context * fix: explain skipped agent context compaction * fix: retain recent agent memory when storage compaction cannot fit * fix: start retained agent memory at a user turn * fix: reject unsafe agent memory truncation on storage fallback * docs: clarify agent context window override scope * fix: keep recent turns verbatim when compaction memory outgrows storage Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix: keep the compaction summary out of the agent's answers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * fix: shorten the agent context window help text Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJyjUmidDYV2m6ifQdLeH * chore: update ee-repo-ref to 942d4013f36edac1fc9a9addbdb02198db1c7a05 This commit updates the EE repository reference after PR #812 was merged in windmill-ee-private. Previous ee-repo-ref: 8ca1682ce6106ba6ea96894fbe606dac64102eb6 New ee-repo-ref: 942d4013f36edac1fc9a9addbdb02198db1c7a05 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
cf5c49c3dc |
feat: restart perpetual runs on the version a deploy makes runnable (#11200)
* feat: opt-in move of perpetual runs to a newly deployed script version Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep the perpetual-run opt-in across relocks and check the new version's tag Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: pin the tag check on a perpetual version switch Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: count perpetual runs past the first queue page in the deploy prompt Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: restart perpetual runs on the version a deploy makes runnable Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: claim a perpetual run and queue its replacement in one transaction Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: honour a cancel that lands after the worker last read its queue row Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: leave the lost-cancel fix to its own PR and match the scale down to 0 wording Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: run a preprocessor the deployed version adds over the arguments carried over Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * style: shorten the wording of the modal's argument warning Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: never preprocess a restarted perpetual run, as every other restart does Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: preprocess for a replacement whose run had not been through one Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: push a perpetual replacement without the deployed debounce settings Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: name the runs the deploy button restarts Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * perf: skip the perpetual restart lookups on a deploy that is not perpetual Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: move perpetual runs before anything that can fail after the deploy commits Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: report cancellation on the queue listing so the deploy prompt can skip it Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: pass the tag workspace to the availability check after the merge Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * style: say which arguments are defined differently and which values are kept Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: resolve a dynamic tag before checking it for a restarted run Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: pin that a deployed dynamic tag is checked as it resolves Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8741843d2e |
feat: external instance cluster for data tables and Ducklake catalogs (#11197)
* fix pg_dump stuck on version 17 on nix * fix(datatables): refuse a malformed role annotation instead of ignoring it `-- Role operator`, `-- role operator;` and `-- role operator -- why` all failed the annotation parser's exact-match rule, so the query fell through to the data table's default role and ran, silently, under a login the author did not choose. Naming a role exists precisely to not do that. A leading comment whose first word is `role` is now an annotation attempt: the keyword matches case-insensitively, one trailing `;` is tolerated, and anything else is an error naming the line. Only callers that already know the target is a `datatable://` reference ever run this, so ordinary SQL keeps its comments. Also bumps the dev shell's postgres client to 18 — it trailed the server the dev database runs, which takes out every data table export, clone and fork-with-data. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): refuse a malformed role query string instead of ignoring it `?Role=analytics`, `?role=` and `?x=1&role=…` all fell through the reference parser's exact-match rule, so the connection resolved to the data table's default role and ran under a login the caller never asked for — the URI half of the same trap as a malformed `-- role` annotation. The key now matches case-insensitively, and anything else in the query string is an error naming it; `role` is the only parameter a reference takes. Callers that only need the entry keep a lenient `datatable_ref_name`, since they never act on the role. The DuckDB `ATTACH` parser propagates it rather than attaching under the default. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR * fix(datatables): carry the role annotation into the row_to_json retry The retry rebuilds its SQL from `pruneComments(code)`, so the leading comment block never reached the second attempt — and with it the `-- role <name>` line that decides which login the query runs as. The retry connected as the data table's default role instead, so a query the first attempt was denied could succeed on the second, reported as "recovered with the row_to_json fix". Carry the leading comment block over. The retry itself is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * chore(datatables): don't mount the roles UI until the ACL editor lands Enforcement ships first. The permissions drawer is what turns roles on, and the catalog section is what creates them — both are only useful once there is a way to grant a role the privileges it needs, which arrives with the ACL editor. Left mounted they would offer a feature whose other half does not exist. The two components are complete and reviewed; only their call sites here are commented out, with a note pointing the follow-up PRs at them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * fix(datatables): honour `-- role: x`, and fix the DuckDB attach test Two review findings, both real. `attach_datatable_parses_name_and_role` never compiled: `parse_attach_datatable` returns `Result<Option<_>>` now and one call site kept a single `unwrap`. Its `?Role=analytics` case also asserted a refusal, contradicting the parser in the same commit, which matches the key case-insensitively. Replaced with the cases that are genuinely malformed, and a positive one for the cased key. `-- role: analytics` fell through to the default role — the silent fallback the strict parser exists to remove, for the spelling most likely to be typed. The keyword now accepts an optional colon, attached or spaced, while a word that merely starts with it (`rolebased`) is still not an attempt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * fix(datatables): clone a fork's pointer instead of failing after the copy Forking a fork with cloning left an orphan database. The preflight resolves the pointer and sees the governing entry, so both endpoints ran and filled the new database; `apply_forked_datatable` then refused the inherited pointer and rolled the fork back, stranding a registered `wm_fork_*` that no entry names and whose name blocks the retry. Refusing earlier would have been the smaller change, but forking a fork and cloning worked before pointers existed, so it would trade an orphan for a regression. Resolve what the pointer names and write the terminal entry the clone needs: the whole `database` object rather than a patch of its `resource_path`, since a pointer has none, and `reference` removed with it. Also accepts `-- role=x` and `-- Role = x`, two more spellings that fell through to the default role. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * fix(datatables): refuse to roll back the catalog while roles exist The down migration dropped the table and left every role behind: live Postgres logins whose passwords only that table carried, so after a revert Windmill could neither use, disable nor delete them, and re-applying could not recreate them because the names were taken. Cleaning up here is not possible either — dropping a role means reassigning what it owns in every instance database, and a migration runs in one — so it now refuses while the catalog is non-empty and says to delete the roles through instance settings, which does the cluster work. Also enforces the instance-only invariant the resolved-pointer clone relies on rather than only asserting it in a comment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * refactor(datatables): settle clonability in one place, before anything is created A clone is three stages a workspace apart — `create_pg_database`, then `import_pg_database`, then `apply_forked_datatable` inside the fork transaction. Only the third can roll back, and `CREATE DATABASE` is not transactional, so any refusal that lives there strands a registered `wm_fork_*` that no entry names and whose name blocks the retry. That orphan has now been fixed three times, most recently reintroduced by a guard added one commit ago. Patching each new refusal into the first endpoint is not the fix; having two places that can refuse is. `ensure_datatable_is_clonable` now answers every reason a copy can be refused and returns what it resolved, and the stage that writes the entry only does the work. Also takes an ACCESS EXCLUSIVE lock before the rollback guard counts, so a role created concurrently cannot slip between the check and the drop. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb * fix(datatables): let a retried clone reclaim its own leftover database A clone creates its target database one request before it copies into it, and the fork that would name it is written a request after that. Any failure in between — a pg_dump error, a bad restore, a dropped connection, the source's roles changing mid-flow — left a registered `wm_fork_*` that no entry names, and every retry then failed on its name. This predates data table roles. `create_pg_database` now reclaims such a leftover before creating: only a `wm_fork_*` database Windmill registered as a data table database and that no data table or ducklake entry names, in any workspace, archived ones included. The drop never terminates connections, so a clone still copying into it makes the reclaim fail instead of being cut off. It is limited to callers who administer the source — reaching it is not enough, since on a data table without roles every member reaches it — and anyone else gets the refusal an existing database always got. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Revert "fix(datatables): let a retried clone reclaim its own leftover database" This reverts commit |
||
|
|
797147ea7a |
fix: report a worker's last job when it ran under one poll interval (#11399)
* fix: report a worker's last job when it ran under one poll interval Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: share the unreported job slot with the interactive worker shell Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: pin that a main-loop ping without a job keeps the last one Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
649c43e7c1 |
fix: run an AI agent tool on the worker its own tag selects (#11370)
* fix: run an AI agent tool on the worker its own tag selects Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: run a tagged agent tool inline when this worker serves its tag Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: give an inline agent tool a job token of its own Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: report a lost tool wait to the model and cancel tools on agent timeout Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
65cba2dbb7 |
perf: advance a flow step with one v2_job_status update (#11357)
* perf: advance a flow step with one v2_job_status update Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: state what advance_flow_status returning None means Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep the merged flow advance identical for rows without a status row or with a malformed status Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FbA4shbpCnRsUxir1GEfm * docs: note the JSON null invariant behind the empty-path no-op Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FbA4shbpCnRsUxir1GEfm --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
53a5cfd17a |
perf: skip job-start pings and checkpoint read for short non-WAC jobs (#11356)
* perf: skip job-start pings and checkpoint read for short non-WAC jobs Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: pin the wac language gate alongside is_wac_v2 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep the start memory sample for jobs shorter than one poll tick Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
e14da5c6bc |
feat(bedrock): add OIDC role assumption as a fourth auth mode (#10936)
* feat(bedrock): add OIDC role assumption as a fourth auth mode Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn * fix(bedrock): gate the OIDC cache correctly and assume the role once per job Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn * refactor(bedrock): check the OIDC region before minting a token Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn * fix(bedrock): keep OIDC session names collision-resistant, gate the copy on EE Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn * fix(bedrock): check the OIDC region before reusing cached credentials Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn * fix(bedrock): clear assumed-role sessions when AI settings change invalidate_ai_request_cache_for_workspace cleared AI_REQUEST_CACHE only, so a workspace's AI settings edit reset one cache and left the assumed-role sessions keyed on the old config in place until STS expired them. Also name the region requirement in the credentials-check hint, so following it does not land on the OIDC path's region guard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YCVb91fZp3dqM14KRPzoEn * chore: update ee-repo-ref to de73db2bacfdc3eaa2e63b1827178bc198d54e5c This commit updates the EE repository reference after PR #770 was merged in windmill-ee-private. Previous ee-repo-ref: c43dab1e69b1cb3f685e6df07bff634dc2a0b734 New ee-repo-ref: de73db2bacfdc3eaa2e63b1827178bc198d54e5c Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
d3d5392917 |
feat: add an options field to the postgresql resource (#11223)
* feat: add an options field to the postgresql resource Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep a literal plus in postgres connection string parameters Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: pass postgres options to trigger connections Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: read DATABASE_URL options the way sqlx does Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: include postgres options in databaseUrlFromResource Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
bc4f872f10 |
perf: skip the flow_env ancestor walk for sub-flows with nothing to inherit (#11349)
* perf: skip the flow_env ancestor walk for sub-flows with nothing to inherit Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep the flow_env mark out of sub-flow definitions replayed by restarts Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: align the guest scopable-path test with app path validation Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
69fafb9262 |
feat: check dynamic worker tags on the tag they resolve to (#11271)
* feat: check dynamic worker tags on the tag they resolve to Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: pin the tag check after a flow preprocessor resolves the tag Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep tied tag placeholders exact and drop an unresolvable flow tag Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: describe tied tag placeholders in the custom tags editor Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: check workflow-as-code task tags on the tag they resolve to Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: judge a preprocessed flow's tag as written when it is run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: check a preprocessed flow's own tag, as written only where it reads args Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: explain dynamic tags in the worker tag picker Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: look up worker availability for typed static tags Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: poll worker availability with the tag list as it is at each run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: warn on custom tags with nothing fixed at their start or end Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: open the broad custom tag warning from the keyboard Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2699d5065e |
fix: check workflow-as-code task tags against CUSTOM_TAGS (#11273)
* fix: check workflow-as-code task tags against CUSTOM_TAGS Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: resolve and check every WAC child before the parent parks Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: build parent-code WAC children at push time, not in the pre-pass Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
53afecd458 |
fix: register the job token with the sensitive log masking system (#10943)
* fix(worker): register the job token with the log masking system The masking system covered secrets fetched through `get_value_internal` and `$encrypted:` args, but not the job's own token, so a script that echoed `$WM_TOKEN` wrote it verbatim into logs that are persisted to the database and, when configured, to object storage. `run_worker` now registers the token for the job it just pulled, alongside the existing `register_running_job` call, so it is redacted like any other registered secret. That makes every job carry at least one registered value, where before the per-batch mask snapshot was skipped entirely for the majority of jobs that touched no secret. Cache the compiled Aho-Corasick automaton per job and invalidate it when a new secret is registered, so a chatty job no longer rebuilds it once per log batch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P316wKe2QCYNcdsx1PwAJ3 * fix(nativets): mask secrets in the in-process log path NativeTS hands `console.log` output to a task that drains a channel into `append_logs`, so it never reaches the masking in `handle_child::write_lines` and a script logging `$WM_TOKEN` persisted the raw JWT. That drain can still be flushing after the job is unregistered, so a plain per-line `snapshot` would leave the tail unmasked. `JobMasker` keeps the last masks it saw for exactly that window, and refreshes while the job is alive so secrets fetched mid-run are covered too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P316wKe2QCYNcdsx1PwAJ3 * fix(nativets): seed the job masker at construction A `JobMasker` that only looked up its masks on the first `mask` call had the same hole at the head of the log that its retention closes at the tail: if the drain task's first productive poll landed after the job was unregistered, the registry was already gone and every line was written raw. `new` now takes the snapshot, and its callers construct it from the job's own execution while the job is still registered. Also cover the nativets sink with an integration test, gated on `deno_core` the way the CI test build is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P316wKe2QCYNcdsx1PwAJ3 * fix(nativets): mask on the producing side of the log channel Masking as the drain task wrote to `append_logs` left two holes, because that task is detached and outlives the job: a secret registered mid-run could still be queued when the job was unregistered and would then be written raw, and the `windmill:job_log` tracing emission that EE forwards job logs on never went through the mask at all. Mask where the line is produced instead. That loop is joined before the job completes, so the job's secrets are always still registered, and one call now covers both the tracing mirror and the channel. The result stream keeps reading the raw text, the way `handle_child` keeps its raw `line` for results. `JobMasker` is no longer load-bearing for the post-unregistration window, so it is documented for what it now does: keep the security notice to once per set of secrets for a sink that masks line by line. Also drop the nativets test's tag override — `DEFAULT_TAGS` does advertise `nativets`, so the comment justifying it was wrong — and pin the automaton cache invalidation, whose failure mode is an unmasked secret. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P316wKe2QCYNcdsx1PwAJ3 * fix(worker): hold the log-masking lifecycle at the job boundary Registering the job around the poller's call left every other way of running a job uncovered: the interactive worker shell and inline AI agent tools both call `handle_queued_job` directly, and a script logging `$WM_TOKEN` from either persisted the live credential. Register from inside `handle_queued_job` instead, under a drop guard, so each path is covered by construction rather than by remembering to add a call. Nothing is lost by unregistering earlier: the writes that follow go through `append_logs`, which never consulted the registry. In nativets, decide the stream/log routing before masking. `MaskSnapshot`'s notice is one-shot, so a secret-bearing `WM_STREAM:` chunk used to spend it on text that is then discarded, leaving later redactions in `job_logs` unexplained. Restore the masker's post-unregistration test: the memory-limit path never joins the producing loop, so that fallback is still load-bearing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P316wKe2QCYNcdsx1PwAJ3 * docs: correct the nativets masking comments The producer loop is not joined on the memory-limit path, so it does not "always" run while the job is registered — say normally, which is what `JobMasker`'s fallback is there for. Name the reason a stream chunk stays raw everywhere it goes, including the tracing mirror: it is result data that no log sink persists, so masking it would be masking a result. State the masker test's invariant without asserting a mechanism behind it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P316wKe2QCYNcdsx1PwAJ3 * fix(masking): keep the security notice on a line of its own `mask` appended the notice as a newline plus the notice text, which assumes the caller hands it a bare log line. nativets hands it a chunk that already ends in a newline, and its sink concatenates chunks verbatim, so the notice arrived after a blank line and the next log line was welded onto the end of it. Emit the notice as its own line for either shape. `handle_child` is unaffected: its input never ends in a newline, so it keeps the original path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P316wKe2QCYNcdsx1PwAJ3 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a08992834d |
feat: render an AI agent result as its answer, not as raw JSON (#11051)
* feat: render an AI agent result as its answer, not as raw JSON Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: sanitize agent markdown through the shared plugin chain Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: fold agent stream events incrementally per poll Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: separate the agent meta line from the result toggle group Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: add a transcript view of an agent run's conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: retire AIAgentLogViewer in favour of the transcript Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: show the partial transcript a max-iterations failure carries Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: move the agent meta line and system prompt below the conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: show an agent run as what it did, not as a conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: name the agent run breakdown a trace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): label the save-as-agent form fields per the guidelines Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): reuse the resource form's path and description fields Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): keep the action tags on a max-iterations failure Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): keep offline replay inert and the streamed answer to one turn Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): reset the streamed answer on providers that skip tool_call Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: carry the event type narrowing through the stream parser Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: survive a malformed message rather than take the viewer down Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: coerce agent messages once at the parse boundary Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: show an agent run as one scroll ending in its output Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep every streamed turn instead of dropping the narration Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: share the chat divider and drop the unsafe run auto-scroll Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: give the labelled divider a border colour and the standard pretty icon Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: pad the agent run below its badges as well as above Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: follow a streaming run's pane without moving the page Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: share the chat's stick-to-bottom mechanics with the agent run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the answer's citations and end a turn's reasoning with the turn Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: read the agent stream through the windmill-chat sdk parser Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: show an agent run's thinking instead of falling back to raw json Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop a stream the fold cannot use from claiming the run pane Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: identify an agent run by more than the job id the replay withholds Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: drop the tests and comment lines that were not earning their place Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop a streamed turn's text shifting when a tool call closes it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
48f00259c5 |
feat: support $flow_expr[...] dynamic tags on flow steps (#11170)
* feat: resolve $flow_expr[...] dynamic tags on flow steps Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: run an unresolvable $flow_expr tag on the default tag and give it the step input scope Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: resolve $flow_expr tags by path lookup instead of expression evaluation Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: say a $flow_expr tag fails to resolve, not to evaluate Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): word the $flow_expr help like the other dynamic tag lines Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reject a malformed $flow_expr placeholder instead of queueing its literal tag Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: render $flow_expr tag values through the $args path lookup Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: pin interpolate_args through the shared tag path rendering Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0e807fb1dd |
feat: put a data table's connection under Postgres roles (#11020)
* feat(datatables): put a data table's connection under Postgres roles
A data table backed by the instance database resolved to exactly one Postgres connection,
`custom_instance_user`, for everyone who could reach it at all. There was no way to say
this job reads, that one writes, this one never sees the salaries table.
A data table role is now a real Postgres login on the cluster, defined once for the
instance by a superadmin and named exactly as they named it. A script that declares
`-- role analytics` connects as `analytics`, and Postgres decides what it may touch —
grants are ordinary SQL. Windmill answers only "may this caller ask for this role", from
the tenant lists on the data table entry: `u/alice`, `g/analysts`, `f/finance` or `*`.
A data table with no `permissions` block behaves exactly as before.
Everything that opens a connection on someone's behalf goes through one chokepoint,
`get_datatable_resource_from_db`, which takes the identity explicitly and fails closed when
there is none. The role logs in as itself — never `SET ROLE`, which a script could
`RESET ROLE` its way out of.
A fork's data table entry becomes a pointer at the workspace that governs it rather than a
copy of it. The settings clone used to hand a fork a byte-identical entry naming the
parent's database, which a fork admin could edit to grant themselves `admin` there; a
pointer has nothing local to edit, and its tenants are evaluated as a member of the
governing workspace, by email. `permissions` is stripped from the workspace export and
ignored on import: tenants name principals of one workspace, and a settings push is not
where an access decision should be made.
Operations that see the whole database whatever the roles grant stay with the governing
workspace's admins: editing the roles, a migration that declares none, and opening a
replication stream for a Postgres trigger or capture.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): gate the paths that reach a whole database as admin
Auditing what still resolved through the unchecked resolver turned up three that act for a
caller and hand back the admin connection: `resolve_pg_source_checked` (behind schema
export, the full-schema read, database creation, import and the forked-database drop), the
connection test, and the schema snapshot a fork clone takes of its parent. On a data table
under roles each let any workspace member — or a fork admin who is nobody in the governing
workspace — read or copy the whole database whatever its roles grant.
All three now require admin reach on the governing workspace. A dump taken under a
restricted role would be a silently truncated copy rather than an error, so refusing is the
only right answer for the copy paths.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): confine roles to the instance database, and stop a fork reaching the parent's bookkeeping
A data table role is a login on Windmill's own Postgres. Nothing stopped a workspace admin
putting a *resource-backed* data table under roles, at which point the executor dialled the
host that resource names — one the admin chose — with the role's real cluster password, and
`CONNECT` is granted to every registered instance database. Both ends now refuse: the
permissions endpoint rejects the save, and the chokepoint refuses to substitute credentials
on a non-instance entry rather than trusting the record it read.
Two more places reached the governing database without answering to it. The initial-migration
generator returned a `pg_dump` of the whole schema to any member. And the migration
rename/delete cascade followed a fork's pointer into the parent, so a fork admin renaming or
removing their own local entry relabelled or wiped the parent's `_wm_migrations` — after
which the parent re-runs every migration from zero. The remote half is now skipped when the
entry resolves into another workspace, which is also just correct: a fork renaming what it
calls a data table changes nothing about the data table.
Also: revoking a tenant now bounces the replication streams of every workspace holding an
entry that resolves here, not only the governing one, so a fork's trigger stops rather than
living on inside its open connection; the instance role catalog and the governing workspace's
tenant lists are no longer returned to someone who cannot edit them; and the tenant rename
dedup collapses non-adjacent duplicates, per role rather than once any role changed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): fail loudly where a role or a pointer can be left half-recorded
Three ways the feature could end up in a state nobody could see or undo.
Creating a role writes the cluster first and the catalog second, but the catalog write was an
`UPDATE` that matched nothing when the instance Postgres settings row was absent — leaving a
live login with a password nobody recorded: invisible to the catalog, un-recreatable because
the name is taken, and un-deletable because there is no entry to delete. It now errors, so
the operation is retryable once the row is restored.
Deleting a workspace only nulls the fork lineage; the data table entries pointing at it are
left resolving to nothing. Sweeping them is not an option — turning a pointer back into a copy
would hand each fork the database outright — so the delete now names the data tables it
stranded, and resolving one says which workspace is missing rather than reporting a data table
this workspace never had.
`InstanceDatatableRole` derived `Debug` while holding a Postgres password; it is now
hand-written so `{:?}` on the catalog cannot put a live credential in a log line.
Adds the two branches the reviews found unpinned: a caller who is not a member of the
governing workspace at all, and `NoIdentity` — the compatibility path for an agent worker that
predates this and sends no job id.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): unbreak two operator messages and two comments that described other code
The two strings this branch added for states an operator hits once — the catalog write that
matched nothing, and the delete that stranded a pointer — were collapsed from their multi-line
form with the indentation left in, so both rendered with a fourteen-space gap mid-sentence.
`list_datatables` claimed to report a chain it cannot follow and then dropped it; it does drop
it, and the comment now says why that is the right place to stay quiet. The non-superadmin
check in `edit_datatable_config` was introduced as also covering references, which it does not
and need not: `reference` is overwritten from the stored entry for every caller before the
check runs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): serialize role catalog mutations, and state each helper's authorization contract
The catalog is one JSON document, so create, rename, enable and delete are all
read-modify-write. Two concurrent creates read the same snapshot, both succeed in the
cluster, and the second write drops the first — leaving a live Postgres login with a password
nobody recorded, which is the exact state the delete path exists to prevent. Every mutation
now runs in one transaction holding an advisory lock across the read, the cluster DDL and the
write, so a lost update cannot happen and a failure rolls the whole thing back. The DDL
helpers take that transaction rather than the pool, which is what makes the lock cover them.
Their statements moved off `sqlx::raw_sql`: the simple protocol is only needed for genuinely
multi-statement SQL, and its future is not `Send`, which an axum handler holding the
transaction requires. Each of these is one statement anyway.
The new cross-crate surface now says what callers must do. `read_role_catalog` returns
plaintext credentials; `create`/`rename`/`set_login`/`drop_instance_role` and
`converge_connect_grants` mutate cluster-wide state; `read_datatable_entry` reads a workspace's
raw config. All of them are superadmin-gated by their current handlers, but nothing said so at
the definition, which is where the next caller looks.
Also: the roles table reloads after a failed login toggle instead of leaving it claiming a flip
that did not land; the rename affordance is the design-system `Button`, not a raw one; and
`resolve_datatable_pg_as_caller` drops a `role` parameter no caller ever filled — browsing
resolves as the data table's default until the database manager grows a picker.
Why role passwords stay a plain `String` while the instance user's password beside them is a
`StringOrSecretRef`, asked three times across reviews: that one is a secret ref because an
operator supplies it and may want it from their own backend, while these are minted here and
never entered by anyone, so there is nothing for a ref to point at. Encrypting generated
secrets at rest is a separate change that would take the replication password with it. Now
said at the field.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): give the role catalog its own row, out of reach of the config machinery
Putting it inside `custom_instance_pg_databases` was the wrong call, and it cost two ways.
The catalog serializes a generated Postgres password per role, and that row is the
operator-facing instance config, so the passwords reached `get_instance_config` and its YAML
editor — a live cluster credential in a response body, a UI field and any log of either.
Worse in the other direction: `to_settings_map` strips the catalog, so a full-row upsert of
that key writes the row back without it and the catalog is gone, while the cluster keeps every
login it described.
`custom_instance_replication_pwd` is the precedent and says exactly why — a generated secret,
written only by the server, never operator-authored, hidden so the config machinery cannot
read, rewrite or drop it. The catalog is the same thing, so it now has the same shape:
`datatable_roles`, in `HIDDEN_SETTINGS`, `PROTECTED_SETTINGS` and the agent-worker denylist.
No redaction to keep in step with three code paths, and no way for a neighbouring write to
take it out.
Two races on the same shared documents. `edit_datatable_config` read the stored data tables
outside its transaction and then wrote the whole `datatable` document, so a permissions save
committing in between was silently rolled back; it now reads under `FOR UPDATE`. And
`set_datatable_permissions` validated role ids against the catalog before opening its
transaction, so a deletion in between let it write a deleted role back — including as the
default, which every later job then fails on; it now holds the catalog lock and the settings
row across validation and write.
Completes the authorization contracts the previous commit claimed but did not finish:
`read_datatable_entry` (which it named and missed), `resolve_governing_datatable`, whose whole
job is to answer for a workspace the caller may not belong to, and
`converge_connect_grants_with`, which had not inherited its wrapper's.
Also the generic Python SDK reference: `_format_py_params` learned the bare `*` last time, but
`extract_py_functions` is a second formatter and still rendered `datatable(name, role)`, so
code written from that page passed a keyword-only argument positionally.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): make the concurrency test pin the handlers, and the contracts describe what is enforced
The concurrency test reimplemented the read-modify-write inline, so deleting the lock from all
three handlers left it green — it pinned Postgres, not the code it was written for. It now
drives `create_datatable_role` twice concurrently and asserts the catalog kept both names.
Checked the way the last one should have been: removing the lock from the handler makes it
fail with "wmtest_a_… is a live cluster login the catalog forgot".
The contracts added last commit were stricter than this PR's own callers, which is worse than
none — the next reader sees a rule already broken and learns to ignore it.
`read_role_catalog` said superadmin-only while two of its four callers are open to any
workspace member, and `converge_connect_grants` said superadmin while
`set_datatable_permissions` reaches it as a workspace admin. Both were fine on substance: the
rule that actually holds is about the credential never reaching a response, log, audit record
or export, not about who may call. They now say that. `read_datatable_entry` gets the same
treatment rather than the one the earlier message claimed for it: it is the primitive every
resolution goes through, so it is deliberately open, and what must not escape is `permissions`
— it names the governing workspace's users, groups and folders.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): close the last ways a role or a pointer can be left pointing at nothing
The raw settings readers hand back whatever is in the row, so moving the catalog into its own
`global_settings` key protected the config machinery and left `GET /settings/global/datatable_roles`
and the settings listing returning every live password. Both now filter that one key. The
neighbouring `custom_instance_replication_pwd` has the same shape and is not touched here: it
predates this and widening the fix to it is a decision about an operator workflow, not a
consequence of this change.
Three ways a save could leave something resolving to nothing:
A permissioned data table could be moved to a PostgreSQL resource. The block was carried across
as a server-owned field, the runtime refuses roles on a resource-backed table, so the save
succeeded and every job afterwards failed. Refused instead — turning roles off first is one step,
and it keeps discarding an access decision something somebody chose.
Renaming a governing data table left every fork pointing at the old name: the data table
disappears from their pickers and their jobs stop, with nothing in the renaming workspace to
suggest why. The rename now follows into the pointers in the same transaction.
Deleting one cannot be followed the same way, so it is reported instead — the response names what
it stranded, the way deleting a workspace does, and the fork's own error already says which
workspace is gone.
Also: `ensure_instance_db_grant_options_unchecked` claimed superadmin while the permissions
handler reaches it as a workspace admin (the same class fixed last commit, one instance missed);
the role entry kept an `instance_config_schema` derive it no longer needs; `write_role_catalog`
was the one writer of that table not stamping `updated_at`; and the concurrency test dropped its
roles only on success — a failing run is exactly the one that creates them without recording them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* refactor(datatables): put the role catalog in its own table, not in global_settings
Five findings across three rounds were all the same choice. A set of live Postgres credentials
was living in `global_settings`, which has generic read, list, write, config-export and CLI
round-trip paths that know nothing about what they carry: the passwords reached the instance
config and its YAML editor, a full-row upsert of a neighbouring key erased the catalog,
`GET /settings/global/{key}` and the settings listing returned them raw, and this round the
redaction that fixed the last two turned `wmill instance push` into something that wipes every
password — a fix breaking the assumption the previous fix made. `POST /settings/global/datatable_roles`
could also empty it outside the lock.
The approved plan offered a table or `global_settings`, so this is the other option it already
allowed rather than a new design. `datatable_role` is a table: no generic settings path can read
it, list it, export it, write it or round-trip it, so none of the five needs a guard. The
redaction, the hidden/protected/agent-denylist entries and the JSON document all go with it.
One row per role also removes the read-modify-write the concurrency work was about: two
concurrent creates are two inserts, and the unique index on `name` is what settles a collision.
The advisory lock stays for the one window rows do not cover — `CREATE ROLE` is invisible to
another transaction until commit, so without it both creates pass their `pg_roles` check.
Also from this round: rename mappings are checked against the configuration they claim to
describe, since fork pointers are rewritten from them — a caller could otherwise submit
`main -> missing` against an unchanged config and repoint every fork of `main` at a name nothing
has, and `A -> B` plus `B -> C` moved what pointed at `A` all the way to `C`. And the warning
naming forks a delete stranded reached the response but not the screen: both the data table
settings save and the workspace delete now show it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): validate a rename against the save it describes, and re-check under the locks
Three from the round, all about deciding on state that could already have moved.
A permission save resolved the data table and checked it was instance-backed before taking any
lock, then wrote under one. A config save committing in between could move the table onto a
PostgreSQL resource — recreating exactly what the transition guard refuses — or rename it, in
which case the write targeted a key that no longer existed and reported success having changed
nothing. It now re-resolves and re-checks on the locked state.
Rename validation checked that the source existed before and the target existed after, which
still accepts `main -> decoy` against a save that keeps both: every fork of `main` then follows
onto a different data table, silently, because it keeps resolving. The rule is now the actual
old-to-new key transition — a source may only survive if another rename took its name, and a
target may only pre-exist if another rename freed it. That also stops two sources sharing one
target, and it admits a swap, which the previous guard refused: `datatables` is keyed by name, so
a swap cannot be done one save at a time, and refusing it was a regression against main. The
pointer cascade now runs in two passes through a temporary name, the way the migration cascade
one layer down already handles the same shape, so `A -> B` with `B -> C` moves each pointer once
from what it named before the save.
The tenant mutators say what they are for: they write an access decision for any workspace named,
with an arbitrary mutation, and exist for the transaction that frees or renames a principal.
Editing a decision on purpose belongs in the permissions endpoint.
Carried in the same change: the stranded-fork list is a field rather than a phrase to grep out of
a success string; the pointer cascade matches with `EXISTS` instead of a `LIKE` over the whole
document, so a workspace whose pointers name something else is not rewritten to a byte-identical
value under an exclusive lock; and `InstanceDatatableRole` drops the serde derives left over from
the JSON document, one of which would emit `pwd`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): cascade on the leave route that is used, gate migrations before the admin connection, and drop a role atomically
The tenant cascade on leaving went onto `/users/leave`. The UI and the generated client call
`/workspaces/leave` — a different handler in a different crate with the same name — which
deleted the membership and left `u/<username>` in the tenant lists. Leaving and rejoining
therefore restored the access the leave was supposed to end, and a later account taking the
username would have inherited it. The regression test drives the route the client actually
calls; without the fix it fails with "leaving kept the tenant".
The migration endpoints authorized too late. `run_datatable_migrations` opened the data table's
admin connection, created `_wm_migrations` and read it before reaching the per-migration role
check — so with nothing pending, nothing was checked at all. Rollback returned before its check
when nothing was applied, and the status endpoint had none. All three now ask, before any
connection is opened, whether the caller can reach the data table as any role at all; which role
a given migration runs as is still decided per migration, and by the executor after that.
Deleting a role committed the cluster drop and the catalog row, then swept the tenant lists in
separate transactions. A sweep failing part-way left workspaces naming a role nothing can connect
as, while the retry answered `NotFound` because the catalog entry was already gone. The sweep now
runs in the same transaction, so the drop, the row and every tenant list commit together.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): refuse to copy a data table that is under roles
pg_dump carries no roles and the import runs with --no-privileges, so a copied
data table arrives owned by the admin connection with no GRANT for any role.
The settings clone brings `permissions` across, so the fork's tenants pass
Windmill's check, connect as the role they were given, and are denied by
Postgres on everything: an entry that reads as configured and answers nothing.
Refuse the copy — in the import endpoint before any data moves, and in the fork
path the CLI takes. Replaying the source's owners and ACLs into the clone is
what lifts this, and is a change of its own. Dropping `permissions` from the
copy instead would be the unsafe half, since the copy holds the parent's rows.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): refuse the clone's database too, not only its data
A clone is two endpoints: `create_pg_database` then `import_pg_database`. Only
the second refused a data table under roles, so a fork asking to clone one
created and registered an empty `wm_fork_…` instance database and then failed —
and nothing collects it, since `drop_forked_datatable_databases` only drops
entries carrying `forked_from` and no entry names this one.
Refuse in both, so the clone stops before a database exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* nit worker error msg
* fix pg_dump stuck on version 17 on nix
* fix(datatables): refuse a malformed role annotation instead of ignoring it
`-- Role operator`, `-- role operator;` and `-- role operator -- why` all failed
the annotation parser's exact-match rule, so the query fell through to the data
table's default role and ran, silently, under a login the author did not choose.
Naming a role exists precisely to not do that.
A leading comment whose first word is `role` is now an annotation attempt: the
keyword matches case-insensitively, one trailing `;` is tolerated, and anything
else is an error naming the line. Only callers that already know the target is a
`datatable://` reference ever run this, so ordinary SQL keeps its comments.
Also bumps the dev shell's postgres client to 18 — it trailed the server the dev
database runs, which takes out every data table export, clone and fork-with-data.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): refuse a malformed role query string instead of ignoring it
`?Role=analytics`, `?role=` and `?x=1&role=…` all fell through the reference
parser's exact-match rule, so the connection resolved to the data table's default
role and ran under a login the caller never asked for — the URI half of the same
trap as a malformed `-- role` annotation.
The key now matches case-insensitively, and anything else in the query string is
an error naming it; `role` is the only parameter a reference takes. Callers that
only need the entry keep a lenient `datatable_ref_name`, since they never act on
the role. The DuckDB `ATTACH` parser propagates it rather than attaching under
the default.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
* fix(datatables): carry the role annotation into the row_to_json retry
The retry rebuilds its SQL from `pruneComments(code)`, so the leading comment
block never reached the second attempt — and with it the `-- role <name>` line
that decides which login the query runs as. The retry connected as the data
table's default role instead, so a query the first attempt was denied could
succeed on the second, reported as "recovered with the row_to_json fix".
Carry the leading comment block over. The retry itself is unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb
* chore(datatables): don't mount the roles UI until the ACL editor lands
Enforcement ships first. The permissions drawer is what turns roles on, and the
catalog section is what creates them — both are only useful once there is a way
to grant a role the privileges it needs, which arrives with the ACL editor. Left
mounted they would offer a feature whose other half does not exist.
The two components are complete and reviewed; only their call sites here are
commented out, with a note pointing the follow-up PRs at them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb
* fix(datatables): honour `-- role: x`, and fix the DuckDB attach test
Two review findings, both real.
`attach_datatable_parses_name_and_role` never compiled: `parse_attach_datatable`
returns `Result<Option<_>>` now and one call site kept a single `unwrap`. Its
`?Role=analytics` case also asserted a refusal, contradicting the parser in the
same commit, which matches the key case-insensitively. Replaced with the cases
that are genuinely malformed, and a positive one for the cased key.
`-- role: analytics` fell through to the default role — the silent fallback the
strict parser exists to remove, for the spelling most likely to be typed. The
keyword now accepts an optional colon, attached or spaced, while a word that
merely starts with it (`rolebased`) is still not an attempt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb
* fix(datatables): clone a fork's pointer instead of failing after the copy
Forking a fork with cloning left an orphan database. The preflight resolves the
pointer and sees the governing entry, so both endpoints ran and filled the new
database; `apply_forked_datatable` then refused the inherited pointer and rolled
the fork back, stranding a registered `wm_fork_*` that no entry names and whose
name blocks the retry.
Refusing earlier would have been the smaller change, but forking a fork and
cloning worked before pointers existed, so it would trade an orphan for a
regression. Resolve what the pointer names and write the terminal entry the
clone needs: the whole `database` object rather than a patch of its
`resource_path`, since a pointer has none, and `reference` removed with it.
Also accepts `-- role=x` and `-- Role = x`, two more spellings that fell through
to the default role.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb
* fix(datatables): refuse to roll back the catalog while roles exist
The down migration dropped the table and left every role behind: live Postgres
logins whose passwords only that table carried, so after a revert Windmill could
neither use, disable nor delete them, and re-applying could not recreate them
because the names were taken. Cleaning up here is not possible either — dropping
a role means reassigning what it owns in every instance database, and a
migration runs in one — so it now refuses while the catalog is non-empty and
says to delete the roles through instance settings, which does the cluster work.
Also enforces the instance-only invariant the resolved-pointer clone relies on
rather than only asserting it in a comment.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb
* refactor(datatables): settle clonability in one place, before anything is created
A clone is three stages a workspace apart — `create_pg_database`, then
`import_pg_database`, then `apply_forked_datatable` inside the fork transaction.
Only the third can roll back, and `CREATE DATABASE` is not transactional, so any
refusal that lives there strands a registered `wm_fork_*` that no entry names
and whose name blocks the retry.
That orphan has now been fixed three times, most recently reintroduced by a
guard added one commit ago. Patching each new refusal into the first endpoint is
not the fix; having two places that can refuse is. `ensure_datatable_is_clonable`
now answers every reason a copy can be refused and returns what it resolved, and
the stage that writes the entry only does the work.
Also takes an ACCESS EXCLUSIVE lock before the rollback guard counts, so a role
created concurrently cannot slip between the check and the drop.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb
* fix(datatables): let a retried clone reclaim its own leftover database
A clone creates its target database one request before it copies into it, and
the fork that would name it is written a request after that. Any failure in
between — a pg_dump error, a bad restore, a dropped connection, the source's
roles changing mid-flow — left a registered `wm_fork_*` that no entry names,
and every retry then failed on its name. This predates data table roles.
`create_pg_database` now reclaims such a leftover before creating: only a
`wm_fork_*` database Windmill registered as a data table database and that no
data table or ducklake entry names, in any workspace, archived ones included.
The drop never terminates connections, so a clone still copying into it makes
the reclaim fail instead of being cut off. It is limited to callers who
administer the source — reaching it is not enough, since on a data table
without roles every member reaches it — and anyone else gets the refusal an
existing database always got.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Revert "fix(datatables): let a retried clone reclaim its own leftover database"
This reverts commit
|
||
|
|
a571117f3f |
feat: store mcp tool call, result and reasoning on flow conversation rows (#11176)
* feat: store mcp tool call, result and reasoning on flow conversation rows Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: keep the failure reason and web search citations on tool rows Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: persist a structured answer as its own row and keep reasoning-only rows from closing a turn Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: keep a failed Windmill tool's error on its conversation row Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: let a structured answer row claim its streamed thinking Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: scope a structured answer's claim to its own turn and document the row fields as stored Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: claim a textless assistant row only within the newest turn Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor: store the thinking that led to a tool call on the tool row Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep tool calls in stream order and scope a structured answer to its turn Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: store the files a user message carried as object-storage references on its row Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(chat-sdk): read a message's attachments and build their download URL Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(chat-sdk): carry a loaded user message's attachments in AI SDK metadata Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: store the model's call and what it got back on every tool row Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: pass no extras in the orphaned-conversation test's message insert Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(chat-sdk): keep a stored JSON null tool result instead of the row text Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: label a tool call the turn finished without as not finished Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep a structured answer's streamed call out of the did-not-finish label Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: write a turn's conversation rows in order and describe stored tool calls Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: await the image answer row like the agent loop's other rows Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(chat-sdk): place a row nothing streamed by its sequence, not at the end Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(chat-sdk): place only tool rows by sequence, keep a closing row last Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(flow chat): show a failed tool's error, and no Retry on a running or stopped turn A failed tool card showed the row label in place of the error the row now stores. The live tool result now reports failures, so a failed call briefly flagged a running turn as failed; a stopped turn, whose last row is the failed tool or the cancelled flow's failure, offered Retry too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
c297ed0052 |
feat: managed memory with an inherited or custom memory id per step (#11118)
* feat: split ai agent memory into agent policy, run memory id and step history * fix: scope string memory ids to workspace and flow, keep nested tool history inputs * chore: update sqlx cache for the flow context query * docs: describe memory id scoping as collision-free rather than isolated * chore: regenerate openflow json after merging main * fix: offer no memory id for legacy manual memory, document linked history inputs * fix: seed provided messages from legacy manual memory and hide its note once set * fix: bypass memory when a provided messages expression evaluates to null * fix: require a user message when provided messages are empty * chore: keep the empty messages comment within the line width * docs: name the history inputs wherever linked steps list their flow-local inputs * docs: keep the memory storage path on one line * feat: managed memory with an inherited or custom memory id per step Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: list a custom memory id in the test run form and name where an inherited one comes from Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep memory id out of the add-field menu and drop the memory id telemetry Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep legacy auto memory without an id working after an untouched redeploy Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: rename step messages to previous_messages and address review Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * style: rewrap comments and docs lines lengthened by the previous_messages rename Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: read agent memory as either a legacy shape or the current one Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: name the memory setting in ignored-input notes and keep conversions honest Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep a legacy memory count unset on open and read a cleared count as off Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: address review on cleared test history and zero-count memory Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: drop flow-local keys from a linked agent resource before interpolating it Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: keep a linked resource's own inputs as fallbacks and note ignored history on image runs Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: restore the linked agent draft tests and log ignored history on every image run Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: resolve the one-of variant from the value when the selected one leaves the list Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: treat zero-count managed memory as off when enabling chat mode and shorten comments Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: stop requiring user_message in the openflow agent contract when previous messages are the prompt Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
02e47de8b4 |
feat: allow git sync auto-pull, promotion and PRs on Pro licenses (#11173)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b51c0eabbe |
feat: stream reasoning summaries in AI agent Responses API steps (#11124)
* feat: stream reasoning summaries in AI agent Responses API steps Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: retry without a reasoning summary a strict gateway rejects Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: request the reasoning summary whether or not the step streams Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: return the OpenAI reasoning summary in the agent step's reasoning Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: retry without a reasoning summary a gateway rejects with 422 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
57a99f66a8 |
feat: rename saved agents from the agent editor and flag broken links (#11147)
* feat: list flows that link a saved agent and flag broken agent links * feat: rename saved agents from the agent editor and repoint the flow * fix: show an unreadable linked agent as not accessible, not missing * fix: address review nits on agent rename and missing-agent state * fix: open content search above modals and keep Escape for it * fix: register content search on the opener's overlay stack * docs: scope the global search z-index comment to the bases it clears * refactor: show linked agents' rename warning as for scripts and flows * fix: keep the failed-lookup rename warning to resources |
||
|
|
e8078f2a96 |
fix: dispatch workflow-as-code tasks from a deployed flow's inline step (#11146)
* fix: dispatch workflow-as-code tasks from a deployed flow's inline step * fix: give a workflow-as-code task its own result-cache key * fix: key a cached workflow-as-code task on its name and arguments * fix: hash a cached workflow-as-code task's arguments like any job's * chore: regenerate system prompts for the task cache_ttl docs * fix: key a cached workflow-as-code task on its step key, not its name * fix: key a cached workflow-as-code task on a fingerprint of its code * fix: keep the task() doc attached to task() * fix: key a cached inline task on its step key and the workflow input * docs: cache_ttl has no effect on a taskFlow target |
||
|
|
9a8a9c480c |
fix: stop reading an array job result as wm_failure or http response (#11154)
* fix: only read wm_failure and wm_labels from an object job result * fix: serve an array sync result as json, not a composite response |
||
|
|
c4e878e831 |
feat: return an ai agent step's thinking in its job result (#11140)
* feat: return an ai agent step's thinking in its job result Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: say when an agent result carries no reasoning Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
31c43255fd |
fix(worker): bound cache transfers and import fetches in bun jobs (#11138)
* fix(worker): bound object-store cache transfers and relative import fetches Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(worker): bound the codebase download and label slow-step warnings Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(worker): log a stalled cache transfer once and ignore a zero cache timeout Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
a78beff743 |
feat: dynamic AI agent toolsets (#11050)
* feat: dynamic ai agent toolsets, and memory as a step input Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review round 1 on dynamic ai agent toolsets Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: tag enabled_tools and drop the memory step input Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: let an mcp server entry be named by the path the roster shows Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep $res: out of the tool names the enabled tools picker offers Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: name an mcp server by its bare path on the one side that can hold it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: count the enabled tool names that matched nothing instead of logging them Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: narrow an agent's roster in one pass, by whole entries Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: pin that an mcp summary is rejected against a name that is not Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: regenerate the copilot flow schema after the merge Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: shorten the enabled tools list hint Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: take enabled_tools back to a plain list of tool names Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: keep the enabled tools add-menu hint describing the unset field Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: name a websearch tool that carries no summary of its own Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: reserve the name web search is enabled by so no tool can share it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: spell the reserved web search name with a hyphen Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: reserve __wm_web_search as the name web search is enabled by Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: advance ee-repo-ref past the git sync ci check work Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: shorten the enabled tools description the run form shows Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to e4c1b794d6c5e6e390987341b2840587bbb40348 This commit updates the EE repository reference after PR #785 was merged in windmill-ee-private. Previous ee-repo-ref: af668462f0f06b02a5f4e0c22e6156858487a518 New ee-repo-ref: e4c1b794d6c5e6e390987341b2840587bbb40348 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
96963080f1 |
fix(python): parse wheel RECORD paths as RFC 4180 csv fields (#11133)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
0b1e9c0dda |
fix: wake a WAC parent from every path that completes its child (#11119)
* fix: wake a WAC parent from every path that completes its child Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L2ibGNBxNd8oa3uQZLHsXn * fix: park a WAC parent before writing its checkpoint so lock order matches child completion Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L2ibGNBxNd8oa3uQZLHsXn * fix: check the parent-child link before touching a WAC parent, wrap the fallback error, keep inline checkpoints in lock order Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L2ibGNBxNd8oa3uQZLHsXn * docs: say the zombie fallback keeps the WAC parent notification in its transaction Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
56e21bce83 |
fix(flows): stop re-evaluating skip_if once a loop is in progress (#11008)
* fix(flows): stop re-evaluating skip_if once a loop is in progress skip_if is a one-time entry gate, but the flow stays at the same step for a loop's whole lifetime, so it gets re-evaluated on every iteration. previous_id stays pinned to the module preceding the loop, but once the loop is InProgress the last completed job is an inner iteration, and the results proxy in windmill-jseval aliases results.<previous_id> to that job's result. skip_if then reads the wrong value and can flip the loop's module to skipped after one iteration. Skip the check once status_module is already InProgress. * fix(flows): match skip_if gate to sibling entry-state allowlists Rewrite the skip_if gate as a positive allowlist (WaitingForPriorSteps | WaitingForEvents | WaitingForExecutor), matching the shape already used by the BranchOne/BranchAll predicate gates, instead of a negative filter on InProgress. Restart-at-iteration also enters as InProgress; document it as a separate case rather than folding it into the aliasing reason, which does not apply there. Add a regression test pinning skip_if to run once at while-loop entry. |
||
|
|
80eba80d6e |
feat(git-sync): gate GitHub PRs on Windmill CI test results (WIN-2051) (#10096)
* docs: add design doc for automatic git-to-windmill pull sync
* docs: add migration plan and implementation phases to git-sync pull design
* feat(git-sync): add auto_pull settings schema and pull enqueue primitive
Adds AutoPullSettings/AutoPullMode/AutoPullStatus on GitRepositorySettings
(workspace_settings.git_sync JSONB), the GIT_SYNC_PULL_SCRIPT_PATH constant,
and should_pull/effective_poll_interval_s helpers with unit tests. Exports the
EE enqueue_git_pull_job primitive. Foundation for repo→Windmill auto-pull.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): poll repos and auto-pull new commits into the workspace
Phase 1 of automatic repo → Windmill sync. A monitor task (EE-licensed,
single-replica via advisory lock) git ls-remotes each auto-pull-enabled
repository ~every minute and enqueues a pull when the tracked branch moves,
reusing the {workspace_id}:git_sync concurrency key so pulls serialize with
in-flight push commits.
- windmill-store: background (no-authed) resolver get_git_repo_head_for_autopull
that resolves the repo resource (incl. $var: refs) and ls-remotes; GitHub-App
repos are skipped here and will sync via webhooks (phase 2).
- monitor.rs: poll/reconcile/persist with optimistic sha advance and failure
status; targeted jsonb update so concurrent settings edits aren't clobbered.
- edit_git_sync_repository: preserve server-owned auto_pull state on UI save.
- openapi: AutoPullSettings/AutoPullMode/AutoPullStatus + auto_pull field.
- frontend: per-repo "Automatically deploy changes from Git" toggle with last
sync status; demote the GitHub Actions link to an advanced CI option.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): wire webhook lifecycle + receiver; share reconcile logic
OSS side of phase 2 auto-pull webhooks:
- edit_git_sync_repository creates/removes the repo webhook on save (EE-gated,
best-effort → falls back to polling).
- monitor poller now delegates to the shared windmill_git_sync reconcile/persist
helpers (also used by the webhook receiver), removing duplicated logic.
- export the shared reconcile/persist/failure helpers; bump EE ref.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref for phase 3 in-app PR creation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): show webhook vs polling status on the auto-pull toggle
When a repo has an active webhook (auto_pull.webhook_id set), the status line
reads "instant via webhook"; otherwise it reads the ~1-minute polling cadence.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): post PR diff check on dry-run completion (phase 4)
Worker completion hook in process_completed_job: when a DeploymentCallback job
carrying the __git_sync_pr_check marker finishes, parse the dry-run SyncResponse
and patch the GitHub check run with the diff summary (success/neutral/failure).
Export enqueue_git_pull_dry_run; bump EE ref.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref (drop unused GHES webhook_secret)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* revert(git-sync): defer phase 4 PR diff checks (OSS side)
Remove the worker completion hook that posted the PR check run, drop the
enqueue_git_pull_dry_run re-export and the orphaned sqlx cache, bump EE ref.
Phases 1-3 (polling, webhooks, in-app PR creation) are unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Revert "revert(git-sync): defer phase 4 PR diff checks (OSS side)"
This reverts commit
|
||
|
|
c90d1d95c2 |
refactor: make the app policy's principal the authority for its identity (#10440)
* refactor: make the app policy's principal the authority for its identity * fix: align the app backfill with the sibling migration and audit the uncached address * chore: refresh the sqlx cache after rebasing onto the merged base * fix: resolve the app execution address uncached, it decides the job's authorization * chore: cache the EE queries at the ref this branch pins * chore: cache the EE queries at the ref this branch pins * fix: derive the app draft's on-behalf-of address on read * chore: cache the query the draft derivation test added * fix: derive the app identity on the draft-table and version reads too * docs: state the draft resolver's authorization contract * fix: resolve a draft's principal against workspace membership only * chore: cache the membership lookup the draft resolver added * fix: drop an unresolvable draft's address instead of leaving it stale * perf: evict the address cache on change so app dispatch can read it * fix: evict on superadmin role changes, not only address changes * refactor: make the app policy's address optional instead of derived on read * fix: follow an external superadmin's rename into the apps that name them * docs: state the removal gate once, and correctly * refactor: drop the app-policy version constant that gated nothing * docs: drop the last reference to the removed constant * perf: read the address cache everywhere now that eviction reaches every replica * fix: keep persisted addresses off the cache the poller evicts asynchronously * docs: state where the cached address is accepted and where it is not * docs: keep the cache rule in one place and drop the stale premise * docs: sort the two lookups by how long a wrong answer lives * fix: resolve the schedule address uncached where it is written to the row * docs: name the release this actually ships in * perf: evict a superadmin's key per workspace instead of the whole cache * fix: evict every alias a superadmin principal can be spelled as * docs: describe the trigger as it is * docs: cover the round-tripped read in the cache rule * docs: record why a stale dispatch address cannot escalate * fix: validate a dispatch address against the principal's live binding * fix: carry the validated address through to the job row and token * fix: record the validated address on the job row, not the one handed in * test: run the substep tag check as the non-superadmin it means to test Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * fix: rewrite a stored app address that disagrees with its principal Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * docs: record the accepted staleness window of the cached dispatch address Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * fix: record the validated address on the job's audit row Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * docs: record the accepted rename race of pre-transaction identity resolution Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * docs: separate the app's stored address from the derived one in the resolver doc Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * docs: describe the job identity fast path the push comments skipped Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * fix: backfill a legacy group-prefixed username as the group it names Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * fix: resolve a schedule edit's identity before opening its transaction Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * fix: never resolve a disabled member to a same-named superadmin Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * docs: state what the email-change notify buys, and rewrap two comment lines Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * fix: keep a group's runnables when offboarding a legacy group-prefixed member Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * fix: read the app author from the stored address, as execution does Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * docs: record the rename race's full consequence as a known, accepted limitation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc * docs: record the keep-target group address case as a known, accepted limitation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
4afb9aa677 |
fix: bundle deployed bun scripts whose only pin is on a dynamic import (#11096)
* fix: bundle deployed bun scripts whose only pin is on a dynamic import Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EH42obCk6WJnc7N4Fa25JH * fix: retry the no-db prebundle too, and guard bundles bun builds as written Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EH42obCk6WJnc7N4Fa25JH * fix: name the bundle retry after the import specifiers it unpins Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EH42obCk6WJnc7N4Fa25JH --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
9fc50a23fb |
feat: make snowflake_oauth work as a dbt warehouse on every engine (#11095)
* feat: make snowflake_oauth work as a dbt warehouse on every engine Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gD7E1cPVhX1kLsB4fAH3b * fix: scope the early token refresh to dbt, never follow jail symlinks Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gD7E1cPVhX1kLsB4fAH3b * fix: lock early token refreshes per account, keep the profile until the new one renders Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gD7E1cPVhX1kLsB4fAH3b * fix: hold the refresh lock until the new token is written Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gD7E1cPVhX1kLsB4fAH3b * fix: poll the refresh lock with a bound instead of pinning a connection Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gD7E1cPVhX1kLsB4fAH3b * refactor: drop the early OAuth refresh from the dbt warehouse route Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gD7E1cPVhX1kLsB4fAH3b * fix: keep endpoint keys like token_uri in the profile identity Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gD7E1cPVhX1kLsB4fAH3b * fix: mask access key ids with the secrets they pair with Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gD7E1cPVhX1kLsB4fAH3b --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
30ffdbecc1 |
fix: unpin only the specifiers in the bundle a bun modules run executes (#11083)
* fix: keep version pins from imported scripts in bun lockfiles Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCq4nkUjiZBnPPMuGYCo6w * fix: strip version pins from the bundle a bun modules run executes Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCq4nkUjiZBnPPMuGYCo6w * fix: unpin only module specifiers, not matching text elsewhere in the script Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCq4nkUjiZBnPPMuGYCo6w * docs: name the raw endpoint lock generation fetches imports through Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCq4nkUjiZBnPPMuGYCo6w * fix: leave require calls alone and skip spans not on a quote pair when unpinning Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCq4nkUjiZBnPPMuGYCo6w * test: create the bun bundle cache dir a dependency job saves into Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCq4nkUjiZBnPPMuGYCo6w * test: drop the lock test #11082's module test already covers Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCq4nkUjiZBnPPMuGYCo6w * fix: narrow the change to a fail-open strip of the modules-run bundle Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCq4nkUjiZBnPPMuGYCo6w * fix: unpin only the specifiers in the modules-run bundle, and log a parse fallback Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCq4nkUjiZBnPPMuGYCo6w --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d539e8674f |
fix(dbt): stop dbt sending anonymous usage stats from workers (#11091)
Claude-Session: https://claude.ai/code/session_01KJZKmyRJZWUmSS7H1vV6Hs Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
57f8b0826a |
fix: keep pinned import versions of imported scripts in bun lockfiles (#11082)
* fix: keep pinned import versions of imported scripts in bun lockfiles Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: guard pinned imports through an unlocked multi-file bun run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |