Commit Graph
14765 Commits
Author SHA1 Message Date
Guilhem LemouelandClaude Opus 5 d6c07ce66d fix(chat-sdk): read a tool's call and a model's thinking off the row
A conversation row carries three things the SDK did not declare and so
never read: what a tool was called with, what it returned, and the
thinking behind an answer. For a tool that runs inside the agent's job
the row is the only copy of the first two, and for a failed tool the
result is what it failed with — so a conversation read back through the
SDK showed a tool call with nothing to expand and a failure with no
reason.

The AI SDK adapter already preferred a tool's result over the row's text
when reporting an error, so it needed only the data.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 13:02:35 +02:00
Guilhem LemouelandClaude Opus 5 680da0a916 fix(chat): keep an answer the server has not written down yet
The worker writes a turn's rows in transactions it does not wait for, so
one can land after the flow reports itself finished. The chat read the
conversation once at that point and then deleted every row the stream
had put on screen, on the assumption the server's copies had replaced
them. When the answer's copy had not arrived, the reader watched their
answer appear and then vanish, and nothing fetched it again until they
reloaded the page.

A row the server sends now takes the place of the one standing in for
it, keeping what only the stream knew — a tool's call and result, the
model's thinking — and taking the server's id, which is what moves the
read cursor past it. A row standing for nothing is appended. Nothing is
deleted, so an answer outlives a write that is late or never comes, and
the chat is never in a position to conclude that it is not coming.

The last read of a turn is repeated a few times while rows the turn
opened are still unaccounted for, and then stopped: what is left
standing is the only copy there is. It waits only on rows that turn
opened, so one an earlier turn left behind cannot make every turn after
it wait the budget out.

Ending a turn settles what it leaves mid-flight. A card opens on a call
and closes on its result, and the result need never come — an agent
answering through an output schema streams the call and nothing else, and
Stop is an end of its own — so without this a card spins for as long as
the chat is open, and the turn can never report itself as failed.

Rows sit in the order they were shown rather than by sequence, so the
cursor is the furthest row a conversation holds rather than the last one
in the list: read from the end, an appended row would move it backwards
and keep it there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 13:02:29 +02:00
Guilhem LemouelandClaude Opus 5 d46de0179c fix(chat): word every tool row from the tool that ran
A tool row said one of four things depending on how its call ended, and
two of those wordings were the error itself. The row's text is the one
thing every reader of a conversation has — the transcript, the chat SDK,
anything reading the API — so it should say the same kind of thing every
time, and the reason a call failed belongs where every other call's
result already lives.

All four now word the row from the tool, and a failure puts what it
failed with in the row's result. Only where the row is the sole copy: a
tool that ran has a job holding both its arguments and its error, and
that job is still what the card reads them from. The card shows a failed
tool's result rather than discarding it, falling back to the row's text
for the rows already written the old way, which keep rendering as they
always did.

A tool whose job ran and failed streamed success where it stored
failure, so the card read as a successful call for the length of the
turn and corrected itself on the next reload. The stream now carries
what the row is worded from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 13:02:16 +02:00
Guilhem LemouelandClaude Opus 5 0332ffdb45 style(chat): run prettier over the agent chat input tests
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 10:08:37 +02:00
Guilhem LemouelandClaude Opus 5 769e7eebbe fix(chat): list a flow's chats under the path its runs record
The editor's chat asked for conversations under the path the flow was
opened at, while a preview run recorded them under the path being
edited. On a flow that has never been deployed those differ from the
first keystroke, so every test chat was written somewhere the sidebar
never looked and vanished as soon as the list refreshed.

`path` is now the path a run records, and what makes the panel a
different panel moves to `identity`. They are not the same question: a
chat holds conversations, a running turn and the rows it is writing, so
it is replaced rather than re-pointed — but replacing it on every
keystroke in the path field would throw away the turn being typed
alongside. `chatFlowKey` is the flow half of that identity and
`chatIdentity` the whole of it, so the panel key and anything filed
under the flow cannot drift apart.

A conversation keeps the path it was created under, so renaming a flow
that already has chats leaves those behind under the old path. That is
the trade: the sidebar follows what runs record, and what ran before a
rename recorded something else.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 10:08:33 +02:00
Guilhem LemouelandClaude Opus 5 404419588c fix(chat): let a turn own every hold it takes on a chat
Three of the last five review rounds were the same defect with a
different trigger: some path decided a chat was free, or stuck, without
the run having said so. `Turn` gave that question an owner, but the
holds taken before a turn exists were left outside it, and the two that
were are what these fix.

The read that opens a conversation now starts a turn before it awaits.
That read is how the chat finds out whether a run owns the conversation,
so it holds the chat while it is out — and Stop, the panel going away,
and a later send all have to be able to reach it. Each ends the turn,
and a read whose turn is over writes nowhere: it can no longer drop the
rows a live turn is writing, nor resume on top of the turn that replaced
it. `#resumeRunningTurn` carries that turn on rather than starting a
second, which would end the read that produced the rows it reasons over.

A read that fails leaves the chat held, since whether a run owns the
agent memory is exactly what could not be read. `#holdUnreadableChat` is
the one place that happens, and it holds only a turn that still holds
the chat — a read outruns a Stop and fails after it, and taking the hold
back then would shut a composer the reader was just handed.

The run request is not tied to the turn's handle, so the whole of it is
a window in which Stop can land, on a turn with no job to cancel yet.
The run it returns is then nobody's: it is cancelled, and the send
reports that nothing ran.

Which turn is running is asked of the server rather than read back to
through the transcript. The kind is checked again here, because the
caller takes that row's job as the turn's: every other kind names the
agent step's own job, and a turn named with that has Stop cancel the
step while the rest of the flow runs on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 10:08:24 +02:00
Guilhem LemouelandClaude Opus 5 2199050518 feat(api): filter a conversation's messages by kind
A chat reopened while its flow is still running has to find the message
that started the turn: it carries the flow job, which is the only thing
that knows whether the run is over. An agent writes a row per round and
per tool call, so that message sits an unbounded number of pages back in
the transcript, and a client reading towards it is searching with no end
it can name.

`message_type` answers it in one request. The filter is applied inside
the paginated subquery, so `page=1&per_page=1&message_type=user` really
is the newest user row rather than the newest row that happens to be one.

`MessageType` carries a fourth kind, `System`, that nothing writes and
the Postgres enum has no label for. A request to filter on it is refused
before the database, so what reaches the caller is a word about their
request rather than an error about an enum.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 10:08:10 +02:00
Guilhem LemouelandClaude Opus 5 dda7ab2f30 refactor(chat): give a flow chat turn a module of its own
A turn was not a thing. What one is made of lived in three places — a
reactive status record, a non-reactive runtime map, and locals inside the
function following the job — so the question every piece of a turn's work
has to answer, "am I still the turn this chat is on?", had no owner. Each
await site answered it again from whatever abort flag it had in scope, and
each new await needed another answer; the counter this replaces was the
eighth such answer in as many review rounds.

`Turn` holds the run, the handle that stops it, the cursor its stream
resumes from, the rows it is writing and the timers it armed, and the chat
keys one per conversation. Work carries its turn and passes `isCurrent`
before writing, so a frame buffered before a Stop, a poll dispatched a tick
ago, or a settle that outlived its own turn all land nowhere instead of in
whatever replaced it. Ending a turn is one call rather than an abort, two
timers and a pair of reveals that every caller had to remember, and the
sites that release one say which turn they mean.

The chat itself is keyed on its flow and workspace, so changing flow builds
a new panel rather than re-pointing the old one. That is what makes the
guards removable: nothing survives a flow change to need them.

Fixes found while doing this, each of which the old shape allowed: a
two-minute polling deadline that outlived its turn and stopped the next
one's reading; a run launched into a stopped turn that nothing could name;
a conversation-list refresh on every poll tick of a new chat's first turn;
a failed page walk that freed a chat whose run it never got to ask about;
and a Stop during a launch that tore down the turn that came after it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 03:59:48 +02:00
Guilhem LemouelandClaude Opus 5 d3344eb3cd fix(chat): read a conversation forward from the start, and clear the polling deadline
A poll with no row to resume from sent no cursor at all, and the messages
endpoint answers that with the newest page rather than the oldest, so the
rows before it were never read and the sweep dropped the temp rows covering
them. Sequences start at 1, so reading from 0 puts every request on the
forward branch.

A read that stops at the request cap is now kept: each batch is a run of rows
from the cursor, so the next tick carries on from where it stopped. The last
poll of a turn has no tick after it, and there its prefix is dropped instead,
since it would sit beside the temp rows showing the turn's start twice.

`startPolling` armed a two-minute timeout it never held onto, and `stopPolling`
cleared only the interval. A turn ending inside two minutes left the timeout
armed to fire into the next turn on the same conversation and stop its polling
part-way, which a non-streaming turn feels as intermediate rows drying up. The
handle now lives on the turn's runtime and is cleared with the interval.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 18:56:35 +02:00
Guilhem LemouelandClaude Opus 5 b76485b0a1 fix(chat): count requests, not loop indices, in the partial-read warning
The counter was the loop variable, which leaves the cap exit one past the last
request and the stall exit on it — so the same expression printed 21 pages for a
cap of 20, in the one message whose job is to say which exit stopped it. It
counts requests that completed.

The constants shared a doc comment that described the cap while sitting on the
page size, and credited it with catching a cursor that stops advancing, which is
a separate check. Each says what it is.

The partial-read rationale is four lines rather than ten, and points at
`loadMessages` for why a reload is the recovery.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 18:11:15 +02:00
Guilhem LemouelandClaude Opus 5 0f2e7a3865 docs(chat): say what a partial read actually costs
The comment offered the next turn's poll as a recovery route. It is not one: the
cursor comes from the last persisted row, and applying none of them is exactly
what leaves it where it was, so the next poll reads the same window against a
conversation that has only grown. A reload is what recovers, and the comment says
so — it changes how the trade-off reads.

The warning blamed the page cap for both exits, so a cursor that stalled on the
second request reported twenty pages. It names which one stopped it and where it
got to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 18:04:20 +02:00
Guilhem LemouelandClaude Opus 5 3e90c8f5d9 fix(chat): a partial read of a conversation changes nothing
Keeping the temp rows on a capped read was half the answer: the rows that were
read got appended anyway, so the reader would have seen the same range twice with
the tail still missing. Reading that stops before the conversation does is not a
picture of it, so none of it is applied — the transcript keeps what it has, the
next turn's poll resumes from it, and a reload refetches.

A cursor that stops advancing takes the same route. It was marked a whole read,
which is the opposite of what it is: a full page came back, so rows almost
certainly remain. Unreachable against a backend that filters on the cursor, but
the two guards should not disagree about what they mean.

The warning carries how far it got, since the conversation id alone does not say
how far behind the transcript is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 17:56:38 +02:00
Guilhem LemouelandClaude Opus 5 ad847bfa78 fix(chat): make the page cap a state the transcript can survive
Stopping at the cap left the conversation looking fully read, and the sweep then
dropped the temp rows standing in for everything beyond it — the same vanished
answer the paging was added to prevent, one order of magnitude up. The cap is a
guard against a cursor that stops advancing, not evidence the conversation ends
there, so a poll that hit it keeps its temp rows and says so.

The paging test now pins the cursor as well as the rows: a second read that asked
for the same page again would have looked right from the rows alone.

Moves the attachment test out of the middle of another test's comment, which it
had split across two unrelated cases.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 17:49:18 +02:00
Guilhem LemouelandClaude Opus 5 81d843a24c fix(chat): give each attachment its own object, and read a long turn whole
Two attachments can arrive under one name — a reader picking `report.pdf` from
two folders. Keyed on the name alone they raced to a single object, so the agent
read whichever landed last twice and never saw the other. Each gets a segment of
its own inside the turn's prefix; the name stays the last segment, so anything
reading a name off the key still sees what was attached.

The messages endpoint answers oldest-first with a limit, so one request only ever
reached the start of a turn that wrote more rows than a page — an agent calling
several tools a round. Its answer was among the rows left behind, and the sweep
then dropped the temp rows standing in for it, so the answer vanished until the
conversation was next loaded. The poll reads on until a page comes back short.

Leaving uploaded objects behind when a send does not run is deliberate, and now
says so: deleting them is a request that can fail on a path already failing, and
a resend uploads its own, so a lost send costs one prefix rather than a growing
number.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 17:42:12 +02:00
Guilhem LemouelandClaude Opus 5 4ee585e07d fix(chat): guard every write-back behind the re-point, and order the conversation locks
The streaming path had the same unguarded gap the settle path was fixed for: its
final poll is awaited and then `endTurn(settled)` runs regardless. Settling
releases the conversation's queue, so a message typed before a re-point was
started against the flow now loaded, writing it into the previous flow's
conversation and agent memory — the thing the forgetting exists to prevent.

The poller and the sidebar's loader were unguarded too. Both are guarded at their
own await now, which covers every caller rather than each call site.

The editor chats keyed off `$initialPathStore || $pathStore`, which still falls
back to the typed path on a flow that was never deployed — and there the summary
field rewrites the path on every keystroke, so naming a new flow emptied the
composer as you typed. They take the editor's stable identity instead, which is
also what a preview run records.

`FOR UPDATE` over several rows takes them in whatever order the plan plans, so
two purges could acquire two conversations in opposite orders. Both sites order
by id. The transaction the lock depends on is now stated on `delete_jobs`, which
takes a bare connection and would silently lose the serialisation on one that
autocommits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 16:17:15 +02:00
Guilhem LemouelandClaude Opus 5 53bb9ce99a fix(chat): make forgetting a flow hold against work already in flight
Clearing the state was not enough: a transcript fetch, a latest-conversation
lookup or a settle tail started before the chat was re-pointed resolved after it
and wrote the previous flow's rows, selection or status straight back — the bug
the clearing exists to prevent, reached through the gap instead.

Each of those now carries the generation it started in and drops what it fetched
if the chat has moved on since.

The re-point test was asserting two things that could not fail — an empty rows
map and an empty conversation list, both empty either way — so it pinned half the
fix. It loads rows now and checks they are gone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 16:06:43 +02:00
Guilhem LemouelandClaude Opus 5 89a797d498 fix(chat): follow the saved flow path, and serialise the second purge path too
Widening `cleanup()` made it destructive on a prop the editor changes far more
often than the flow does: both editor chats were passed `$pathStore`, which is
bound to the rename field, so the teardown that forgets a flow could run while
someone renamed one. They take the saved path instead, as the preview drawer's
other two call sites already do, so forgetting happens when the flow does.

The conversation cleanup in `jobs_export` is the same logic as retention's and
had the same race: it now takes the conversation rows before asking whether they
are empty. One lock order across both paths — messages, then conversation.

The lock reads rather than executes because a checked `SELECT` has no `execute`;
the discarded ids are the cost of the compile-time check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 15:42:02 +02:00
Guilhem LemouelandClaude Opus 5 ec9c4054f3 fix(chat): release a stuck chat, forget a flow on re-point, serialise conversation cleanup
Three separate holes, all in code this PR added.

`endTurn` cleared every flag but `isDispatchingTurn`, so Stop could not release the
hold a failed transcript load leaves behind. That flag gates the whole surface, so
a conversation whose rows never load took New chat and every other chat with it.

The route component is reused between two flows, so the chat is re-pointed rather
than rebuilt, and `cleanup()` only ended turns. The selection survived, so
`selectLatestConversation` found one already open and the new flow rendered — and
would have sent into — the previous flow's conversation and agent memory. Cleanup
now forgets what it holds about a flow.

Two `delete_jobs` calls each removing one of a conversation's last messages could
not see each other's uncommitted deletes, so neither collected the conversation
and nothing tried again, stranding the row and its `ai_agent_memory`. The
conversation rows are taken before the emptiness check, which serialises them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 15:25:39 +02:00
Guilhem Lemouel c820961dcc Merge origin/main into glm/update-chat-mode-ui
#11134 landed squashed, so main carries its changes while this branch carries its
commits. The flow chat components resolve to this branch's, as they did when the
branch was stacked on it: the SDK keeps serving external frontends and raw apps,
and the in-app chat runs on these components.

`agentFormFields` takes main's version — it grew `seed` and the `enabled_tools`
rule there, and this branch never edited it — with `agentStreamingEnabled` and
its tests grafted back, since FlowInput, FlowPreviewContent and the flow page all
call it. `runInput` stays removed; nothing reads it.

Brings the EE pin main already moved to, which is what the backend checks were
failing to compile against.
2026-09-15 15:01:47 +02:00
Ruben FiszelandClaude Fable 5.1 8b4f6220dc refactor: run the flow chat UI on the windmill-chat sdk (#11134)
* refactor: run the flow chat UI on the windmill-chat sdk

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

* fix: structural answer check, poll option and latest run in the chat sdk

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

* fix: count jobless tool rows and the loaded license in the flow chat

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 12:56:07 +00:00
Guilhem LemouelandClaude Opus 5 3b2eb1485c fix(chat): name the run before offering Stop, and hold a chat whose rows failed to load
Two holes in the window between opening a chat and its job answering.

Stop is on offer for that whole window, and it cancels whatever `status.jobId`
holds — which was not set until the answer came back. Pressing it then cancelled
nothing, ended the turn and freed the chat while the run carried on. The run is
named before the question, and unnamed again if the turn is not taken over.

A load that failed used to release the hold, on the reasoning that an empty
transcript has nothing to interleave with. It does: the run owns the
conversation's agent memory whatever the browser managed to fetch. The hold
stays; selecting the chat again retries the load, and Stop is the way out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 14:55:14 +02:00
Guilhem LemouelandClaude Opus 5 19af05b213 fix(chat): never free a chat on a guess about its run
The same rule the streaming path already follows, applied to the three places
that still decided for themselves:

- Non-streaming turns waited on `waitJob`, which stops answering without
  settling: on its fifth failed status request it cancels and returns, leaving
  its promise pending, and the chat busy for the session. They wait on the job
  the same way a stream-less turn does, and `pollJobResult` goes with it.
- A Stop whose cancel request failed still ended the turn, freeing the composer
  while the run it could not reach carried on. The turn is left to its job.
- Loading a conversation's rows was not covered: between opening a chat and the
  job answering, a send was accepted. The hold now spans the load and the
  question, and a failed question is asked again rather than taken as a no.

A turn with more rows than a page still has no user row on the page to name its
job, so it is not picked up. That needs the server to say which turn is running.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 14:38:28 +02:00
GuilhemandClaude Fable 5.1 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>
2026-09-15 14:28:48 +02:00
Guilhem LemouelandClaude Opus 5 de8fd34334 feat(chat): pick a conversation's turn back up when its run is still going
The client was deciding whether a turn had ended from whether its own stream was
alive, which it cannot know. It asks the run instead.

Nothing in the browser survives a reload, so a chat opened while its flow was
running read as idle and the composer took a message — two turns writing one
agent memory. The row the server writes when a turn starts carries its flow job;
that job is asked whether it is over, and if not the turn is picked back up and
followed. The chat is held from the moment the question is asked rather than from
the answer, since a send accepted during that round trip is the thing this
prevents.

A stream resumed this way replays the rounds an agent has already persisted, and
the transcript cannot tell a replayed round from a second one, so the turn's own
rows are dropped before re-attaching; the final poll brings them back.

With that, a turn whose stream dies no longer needs a rule for giving up: it
polls its job until the run says it is over. The retry cap, the cancel fallback
and the settled/unsettled split go — a run that cannot be reached has said
nothing, and Stop is on screen throughout for the reader who wants out.

A turn with more rows than a page has no user row on the page to name its job and
is not picked up; finding it needs the server to say which turn is running.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 14:23:32 +02:00
Guilhem LemouelandClaude Opus 5 3de5a283fa fix(chat): cancel a run nothing can reach, and settle only a turn known to be over
An API that stops answering says the status is unknown, not that the run stopped,
and the run holds the conversation's agent memory. Marking the turn settled there
both freed the composer and flushed the queued message into a flow that might
still be writing.

The run is cancelled instead, which is what makes the chat safe to use again. If
even that cannot be delivered the turn ends unsettled: the chat is usable, but
its queue waits for the reader rather than going out beside a live run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 13:34:53 +02:00
Guilhem LemouelandClaude Opus 5 de876bbd86 fix(chat): settle a turn whose stream is gone, rather than waiting forever
The fallback awaited `waitJob`, which stops answering without settling: on its
fifth failed status request it cancels the run and returns from its inner poll,
leaving the outer promise pending. A turn handed to it never reached `endTurn`,
so the chat and its queued message stayed locked for the rest of the session.

The turn now waits on the job through the client it already has, and always
settles — either the run completes, or it stops answering enough times to call
it unreachable. `waitJob` itself is left alone; its other callers are outside
this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 13:25:44 +02:00
Guilhem LemouelandClaude Opus 5 3d60bf6e04 fix(chat): hand a turn whose stream is gone to its job, not to the composer
Running out of stream attempts freed the composer while the flow kept going, so
the next turn wrote the same agent memory — the thing the retry was added to
prevent, only deferred. The turn goes to `pollJobResult` instead, which keeps the
chat busy until the run reaches a terminal state and settles it on the rows;
`waitJob` cancels the run rather than waiting forever if the API stays down.

The budget is counted since the last attempt that delivered something, so a long
turn that blips once an hour is not treated like an endpoint that has gone.

An abort is checked inside the consumer loop: the SSE reader drains the frames it
has already buffered, and `endTurn` has by then reset the transcript state they
would be applied to.

The give-up toast truncates what it quotes — a 502 body is often a whole HTML
error page.

Drops the `flow_stream_job_id` scaffolding from the re-attach test: `followJob`
never surfaces that id, so the assertion could not fail for the reason its
comment claimed. Stop cancelling the flow rather than the streaming step is
verified against a running backend instead. Also drops the EventSource stub the
test no longer needs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 13:15:07 +02:00
Guilhem LemouelandClaude Opus 5 ec1c01d1bd fix(chat): re-open a failed stream rather than freeing the composer
`followJob` absorbs the server ending a stream on its own clock, but not the
request to open one failing — a restarting server, a 502. Ending the turn there
unlocked the composer while the flow kept running, and the next turn would write
the same agent memory.

Those attempts are retried from the offset already read, so the answer resumes
rather than replaying, and the turn stays busy across the gap. Bounded, so a
genuinely gone endpoint still reports itself instead of retrying forever under a
chat that looks live.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 13:05:18 +02:00
Ruben FiszelandClaude Fable 5.1 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>
2026-09-15 13:02:44 +02:00
Guilhem LemouelandClaude Opus 5 a6ec3914ed refactor(chat): follow a flow chat turn with the SDK's followJob
The flow chat kept its own EventSource loop: re-attach on the server's stream
timeout, resume from `stream_offset`, drop the offset when a retried step gets a
new stream job. `windmill-chat` does the same thing in `followJob`, which this
PR's base makes importable, so the transport moves there and ~190 lines go.

`followJob` is scoped to one job and holds no chat state, so turns still run in
several conversations at once — each drives its own generator, with an
AbortController in TurnRuntime where the EventSource was.

Three things it does that the loop did not: it buffers a chunk that ends
mid-line, where ours parsed the halves separately and dropped the token; it
reconnects on a close that carried no timeout frame, where ours ended the turn;
and it starts a fresh parser when the streaming step changes.

Stop now cancels the flow job rather than the streaming step. `followJob` never
surfaces `flow_stream_job_id`, so the line that overwrote `status.jobId` with it
goes — which is what the comment on the line that sets it always said should
happen, and what keeps the steps after the agent from running on.

`poll_delay_ms` is sent only with an enterprise licence; a CE server ignores it
and warns on every poll.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 12:59:35 +02:00
Guilhem LemouelandClaude Opus 5 0e20a5d751 feat(ai-chat): render flow chat mode through the AI session chat components
The flow chat ran on its own components; this points it at the ones the AI
session chat already uses, so the two surfaces share a transcript, a composer
and a sidebar instead of keeping two of each.

The seam is `ChatViewHost` (copilot/chat/chatViewHost.ts): the view components
read the host rather than `AIChatManager` directly, and `getChatViewHost()`
falls back to the session manager, so the copilot's call sites are unchanged.
`FlowChatViewHost` is the second adapter, over `FlowChatManager`.

What the flow chat gains from the move:

- turns running in several conversations at once, with a status, a queue and a
  Stop per chat, and an unread count on the rail
- attachments, uploaded to the workspace's object storage for the worker to read
- a composer that speaks for the agent steps a message is fed to: the model, the
  thinking effort, and the flow inputs the agent reads straight out of
  `flow_input`; everything else is asked for in a Configure-inputs modal
- tool cards with the call and the result, the model's reasoning, and a step name
  per answer once a conversation holds more than one agent
- Retry, which replays the failed turn's own run arguments read back from its job
- named and renamable conversations, and test chats kept out of the deployed
  flow's list

Backend: conversation rows carry an MCP tool's call and result and the model's
reasoning, which live nowhere else; every row gets a job so retention can empty
it; and the providers parse a non-streaming answer's reasoning.

Stacked on #11134, which keeps the windmill-chat SDK for external frontends and
raw apps while the in-app flow chat runs on these components.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 11:46:45 +02:00
Ruben Fiszelandrubenfiszel 9fe493311d chore(main): release 1.812.0 (#11111)
* chore(main): release 1.812.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.812.0
2026-09-15 11:40:01 +02:00
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>
2026-09-15 11:34:30 +02:00
hugocasaandClaude Opus 5 42f489685b feat: store resource type display names and label hub integrations (#11113)
* feat: label resource types and integrations with hub display names

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

* fix: load hub integration names in the app and flow pickers

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

* fix: load hub resource type names where drawers title a type

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

* feat: store resource type display names and drop the hardcoded list

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

* fix: leave display_name out of the fork comparison

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

* fix: ignore over-long synced display names, move name loaders

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

* fix: share the hub integration list cache, backfill admins only

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

* fix: keep a name over a nameless duplicate, retry failed hub reads

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 11:32:50 +02:00
69e6efd875 fix(git-sync): run auto-pull as the admin who enabled it (#11121)
* fix(git-sync): run auto-pull as the admin who enabled it

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

* fix(git-sync): audit the admin grant fork pulls make

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

* chore: bump ee ref for the post-commit fork grant audit

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

* fix(git-sync): address review nits on the auto-pull stamp

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

* chore: update ee-repo-ref to ccada062c072d7b74894b63863728fd1ef9bdffd

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

Previous ee-repo-ref: 7cee30f0cf12721cba551cd754dc817444810470

New ee-repo-ref: ccada062c072d7b74894b63863728fd1ef9bdffd

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-09-15 10:34:57 +02:00
Alexander PetricandClaude Opus 5 082d897328 stop the variables page opening a drawer for the instance settings hash (#11129)
Claude-Session: https://claude.ai/code/session_01LCt167dnWJ2EVnLpkHATh4

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 10:30:19 +02:00
Ruben FiszelandClaude Fable 5.1 3895e1d579 fix: count jobless tool rows and the loaded license in the flow chat
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 10:22:51 +02:00
Ruben FiszelandClaude Fable 5.1 dd7ee0cdbb fix: structural answer check, poll option and latest run in the chat sdk
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 10:08:53 +02:00
Ruben FiszelandClaude Fable 5.1 77740b278e refactor: run the flow chat UI on the windmill-chat sdk
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 09:47:26 +02:00
Ruben FiszelandClaude Fable 5.1 96963080f1 fix(python): parse wheel RECORD paths as RFC 4180 csv fields (#11133)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 09:45:50 +02:00
Ruben Fiszelandwindmill-internal-app[bot] e3e638f7f5 fix: skip instance group members that are not email addresses (#11128)
* fix: skip instance group members that are not email addresses

* fix: keep provisioned members whose address only proper_email accepts

* fix: judge instance group members by a mirror of the usr email constraint

* fix: fold ascii only in the proper_email mirror, like the constraint

* fix: let the database judge which instance group members usr will store

* fix: cut a derived username to the column width so a long local part can be provisioned

* chore: move the ee pin to the scim member doc fix

* chore: update ee-repo-ref to 0780955effb657807d14f0eb503cba1d49cee007

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

Previous ee-repo-ref: ee6452d489563204a98df883703f78d5e74cdd69

New ee-repo-ref: 0780955effb657807d14f0eb503cba1d49cee007

Automated by sync-ee-ref workflow.

---------

Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-09-15 09:45:10 +02:00
Ruben FiszelandClaude Fable 5.1 75ee497011 fix(cli): stage a rewritten shared lockfile on git-sync deploy push (#11126)
* fix(cli): stage a rewritten shared lockfile on git-sync deploy push

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

* test(cli): pin that a swept shared lockfile is committed as a deletion

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

* chore: bump the git sync hub script to 28969

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 23:16:37 +02:00
Ruben FiszelandClaude Fable 5.1 5dcf40cb4f chore: move the EE pin forward to the commit that claims pending oauth accounts (#11127)
Claude-Session: https://claude.ai/code/session_01GGciSSE5EFMiDf1dFWQq5M

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 23:06:36 +02:00
Ruben FiszelandClaude Opus 5 57a134e2de feat(ai-sessions): share session artifacts with the workspace by link (#11115)
* feat(ai-sessions): share session artifacts with the workspace by link

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pyjp67oR269QAx3b4yf4oH

* chore: cache the shared artifact queries for offline sqlx

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pyjp67oR269QAx3b4yf4oH

* fix: replace a literal NUL byte in the shared artifact body limit comment

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pyjp67oR269QAx3b4yf4oH

* test: pin that a shared artifact is confined to its workspace's path

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

* fix: sanitize shared artifact markdown and validate the artifact id on every route

The shared page renders another member's markdown, so ArtifactBody now runs the repo's rehype-raw + rehype-sanitize chain with the chat's link renderer on top; only the session viewer opts into the chat code block (mermaid, apply button). The link renderer keeps a link's text when its href is empty or unsafe, and the scheme check moves to a tested helper. The status route checks artifact_id like share does, so a NUL is a 400 rather than a 500.

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

* fix(ai-sessions): say which way re-sharing moves an artifact link

The popover offered "Update to v1" when a v2 link was open on a pinned v1, which reads as if v1 were newer. Each direction now has its own sentence and action: a newer version on screen updates the link, an older one shares that version instead, a rename updates the name.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 22:46:26 +02:00
Ruben FiszelandClaude Fable 5.1 e8c02c04cd feat: windmill-chat sdk for chat-mode flows in external frontends and raw apps (#11117)
* feat: windmill-chat sdk for chat-mode flows in external frontends and raw apps

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

* fix: keep streamed answers until persisted, finish turns after history fallback

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

* feat: ai sdk transport and assistant-ui runtime for windmill-chat

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

* fix: finish a turn from the flow result until its answer row lands, hash chat ids without crypto.subtle

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

* fix: judge a turn answered by a persisted assistant row, wherever it was fetched

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

* fix: attribute a turn's answer to its own jobs, keep a local turn when switching conversations

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

* fix: mirror local history on every change, attribute failure-handler answers to the turn

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

* fix: new chat per token string in the React hook, idle after destroy, no reorder on view

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

* fix: recreate the hook's chat on any credential change, namespace local history per user

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

* fix: send the latest inputs from the React hook

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 22:32:45 +02:00
d0cac0807f fix: set the enclosing span's trace context on exported log records (#11123)
* chore: pin the EE ref that stamps trace context on exported log records

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

* chore: pin the EE ref with the sampling-decision test

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

* chore: update ee-repo-ref to 04a9f1efb4a52c79fcd20258b34780c86103d27f

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

Previous ee-repo-ref: d48361d66580618cb7a934d3c5f56a0c7e39ffaa

New ee-repo-ref: 04a9f1efb4a52c79fcd20258b34780c86103d27f

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-09-14 22:15:37 +02:00
Ruben Fiszel a95e950529 feat(cli): list, get and restore trashed items with wmill trash (#11125)
* feat(cli): list, get and restore trashed items from the CLI

* docs(cli): tell agents a sync push deletion is restorable with wmill trash

* refactor(cli): share the ApiError formatting and type trash flags as integers
2026-09-14 20:06:56 +00:00
91e6dc39ce feat: pre-approved cloud accounts: login links, OAuth adoption, setup, and the trial bridge (#10875)
* feat: single-use login links and oauth-claimable pending accounts

* docs: capture the auth surface facts behind login links

* fix: accept stringified email_verified from oauth userinfo

* docs: describe the oauth claim rule in the auth surface notes

* fix: harden login-link redirects and sweep expired links

* chore: bump ee-repo-ref

* fix: keep expired login links a day so an open still reads as expired

* fix: refuse login links for superadmin and devops accounts

* fix: re-check the account's roles when a login link is opened

* feat: pre-approved cloud accounts finish their setup and start their trial from Windmill

* feat: dev-only localStorage opt-in to the cloud UI on localhost

* feat: finish-setup entry in the desktop settings menu

* style: pulse the settings row while account setup is pending; shorter, blue finish-setup entry

* fix: list the configured providers in the finish-setup modal

* fix: open the finish-setup modal after the menu has closed

* feat: finish-setup provider sign-in keeps the session when the provider asserts another address

* chore: pin the EE companion commit

* fix: plain toast for the finish-setup refusal

* style: format the dev cloud override

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

* feat: onboarding skips the source question an invite already answered

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

* fix: type the finish-setup icons and login_type as the frontend uses them

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

* feat: invited accounts get a workspace name, hub picks and starter prompts from their invite

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

* fix: the workspace form reads the invite's name itself, so the picker prefills it too

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

* feat: an empty workspace offers the projects its invite picked, one click from importing

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

* style: picked projects get identical import buttons

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

* feat: a pinned sidebar banner until an invited account has credentials of its own

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

* style: the account-setup row speaks the rail's language, tinted not filled

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

* chore: pin ee-repo-ref to the import fix

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

* refactor: picked projects live in the template picker only; account-setup row moves to the rail footer

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

* fix: review round — no portal login for job tokens, finish-setup failures keep the session, prompt labels deduped

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

* fix: CI round — trial start is a POST, profile cache follows the session, setup row on MenuButton

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

* fix: CI round — no password road where password login is off, cache note on the login form, trial refusal surfaced

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

* fix: CI round — set_password guarded on its read, refusal stays on the page, docs and formatting

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

* fix: CI round — popup OAuth clears the profile cache, portal helper crate-private, refusal toast stays

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

* fix: a refused trial is recorded inline in the rail, not in a day-long toast

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

* fix: the refusal notice uses the rail's button and has a collapsed form

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

* fix: CI round — SSO can finish account setup, with the same mismatch refusal as OAuth

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

* fix: SSO finish-setup rides in RelayState and the refusal notice is a status region

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

* fix: keep the finish-setup cookie beside RelayState, hoist the status region, pin session-keyed profile cache

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

* fix: empty live region for the trial refusal, drop the setup cookie once adopted, telemetry inventory

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

* fix: the trial refusal survives the responsive sidebar swap

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

* fix: the trial refusal is shown to the account it answers, modal open prop is required

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

* feat: an invited account skips the whole onboarding survey

Its source is the invite and its use case was researched before the invite went
out, so neither question is asked: the known source is recorded and onboarding
opens on naming the workspace. Accounts without an invite profile see the survey
exactly as before.

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

* fix: an invited account with a workspace leaves onboarding before anything paints

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

* fix: account-setup state resets on sign-out, onboarding shows a loading state while it settles

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

* style: keep the refresh doc comment on refresh

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

* fix: profile lists are distinct, and the offer table notes what a users-import does to it

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

* chore: update ee-repo-ref to 1ba6fe83451f0a1f8fafe04b7187087d51e0f769

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

Previous ee-repo-ref: be42722d09832ffff709a1f710f3e97e34d513b2

New ee-repo-ref: 1ba6fe83451f0a1f8fafe04b7187087d51e0f769

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-09-14 20:04:58 +00:00
Ruben FiszelandClaude Fable 5.1 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>
2026-09-14 18:51:21 +00:00
GuilhemandClaude Fable 5.1 a4ddbd733b lower the workspace creation handover floor to 500ms (#11109)
Claude-Session: https://claude.ai/code/session_015hBkusKsmiRiZ9VZVE314G

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 19:28:57 +02:00