The card's form and the deployed page's are now live at the same time, and only
the card took the manager's submission claim. The call stays pending until
`adopt`, a round trip after the page sends its request, so a Run on the card in
that window passed the claim and started a second job — with its own arguments,
and unmentioned in the chat, which followed the card's.
The page takes the same claim before it sends, and hands it back when no job
came of it so a failed start can be retried. A successful start needs no
release: adopting the job settles the call. Whichever surface is first owns the
call; the other sends nothing and says so.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The run page and runs list now mount in process (#11721, #11724), which gives
`/run/<id>` a slot of its own instead of resolving to an iframe. The tab tests
here read better for it: a tab showing a run is `kind: 'run'`, so it cannot
match the viewer test that routes a proposed run to an item's page, and the
card keeps the form as intended.
`Run again` arrives with it, seeding a deployed page's form from the tab url's
fragment. The detail pages now read both that and a chat call's proposal at
init, the proposal last — re-pointing a tab rebuilds its url without the
fragment, so in practice only one of them seeds — and `ItemViewerView`'s load
key takes both, since either changing has to rebuild the form.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A reader who declines a proposal and asks for different arguments still has
that item's page open, and the next proposal belongs on it — but it was parked
on the card until Preview was pressed again. `requestRunArgs` now routes the
call to that page itself, through `openRunOnDeployedPage` with
`onlyIfAlreadyOpen`: a tab already on the item is re-pointed, and anything else
— a tab showing a run included — answers no and leaves the form on the card.
The card keeps a working form while a page carries the call, rather than
reporting that its inputs are elsewhere. Only the chat's own form in a tab is
exclusive with it, since that one is the same draft and two mounts each reorder
the schema in place; a page builds its form from a copy of the proposal and
shares nothing. So the card folds its row instead of emptying it, and expanding
it is how a run goes from the transcript. `isRunFormTabOpen` answers that
narrower question, and the collapse moves into the row's default so pressing
Preview and being routed here behave alike.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The banner was its own block directly above the form, so it sat flush against
the first field. It now shares the row that carries the JSON toggle, where the
existing input badges get their spacing from `min-h-12`.
It goes ahead of the saved-input badge, whose reserved slot stays mounted while
hidden and would otherwise push the banner off the left edge. The row's gate
takes `pendingRun` as well, since a runnable with no schema properties renders
no row and would drop the banner with it.
The badge's reject button also takes the alert palette it sits on, through
`tone`, instead of the neutral bordered one it had from the deprecated
`color`/`size` props.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: mount the runs list in process in ai session previews
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: tell runs list tabs apart by filters and reseed on re-point
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: remount a hosted runs list only when moved from outside
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: data table migration tools and role guidance in ai sessions
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: shared data table guidance and safer migration tool failures
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep a migration that fails to apply and let the session rewrite it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: rewrite only a pending migration and qualify fork isolation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: mount the run page in process in ai session previews
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: let run again fire repeatedly and re-pin an open script tab
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep a run opened through a share link in its own frame
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Redaction only visits the leaves the schema it is given declares, and the page
runs the deployed version as it stands when Run is pressed. A deploy between
this call reading the schema and that press can add a file argument ours never
had, and its bytes then passed unredacted into the persisted card and the
model's result, under the size limit that would have caught a large one.
`adopt` hands over the schema it ran against alongside the job and arguments,
and both redactions use it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closing the JSON editor when the form starts refusing writes mounted
`SchemaForm` in its place, and `PasswordArgInput` mints a variable from the
literal it opens on without consulting `disabled` — so a reader who typed a
plaintext secret into JSON view had it stored by the act of entering plan mode,
with no run and no Run press.
The form no longer swaps sides while it refuses: `JsonInputs` takes `readOnly`
and the editor stays mounted and uneditable. The JSON toggle is already hidden
in that state, which is what keeps the fields from mounting from the other
direction.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: bump frontend python parser wasm packages to 1.830.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: bump cli python parser wasm packages to 1.830.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep the app editor's parent_version out of deployed apps
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: drop parent_version when deploying an app draft
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Two findings on the deployed page carrying a chat call.
`RunForm.run()` mints a variable per `password` argument before it calls
`runAction`, so the plan-mode gate in the detail pages ran after the write it
was there to prevent — and the keybinding reached it without the button. The
refusal moves into the form, as `runRefusal`: set, it takes no writes at all,
its submit buttons are disabled, and `run()` says so and returns before the
secret processing. The page also stops offering the JSON editor under plan
mode, instead of leaving one mounted that silently discards what is typed.
An adopted job's arguments are the page's, and the card had no way to show
them: it kept rendering the proposal, so a reader who edited a field saw the
wrong values and the model was told nothing. `adopt` now hands them over with
the job id, and they go through the same redaction and size guard as a run
this tool starts.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: connect to a data table with every role of the job's viewer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: key a cached result by the run's recorded viewer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: pin the per-viewer cache key and fix a stale doc sentence
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: length-prefix the viewer in the result cache key
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: drop data table role set logins unused for 30 days
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: pin the ee ref with the set login sweep fixes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: pin the ee ref with the sweep retry fix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: give the cache key test its own migrated database
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: add the viewer's roles to a data table connection that names none
The default role of a data table under roles is now a floor: a connection
naming no role also holds the roles its viewer is a tenant of, unless the
default is admin. This replaces the explicit as=viewer option, which is
removed from the reference parser, the executors and both clients. The roles
drawer warns while the default is admin and confirms before removing the
default role.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: let a data table have no default role
A connection naming no role then holds its viewer's roles only and is
refused without any. The roles drawer offers the choice, states what an
unnamed connection gets for the current settings, says who gets admin while
it is the default, and notes who owns a table created over several roles.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: start a data table's roles with admin open to everyone
Turning roles on from the drawer seeded admin with no tenant, which refused
every non-admin member's job as soon as it was saved. It now starts open to
everyone, so the toggle changes nothing until the roles are edited, and the
drawer warns for as long as everyone can connect as admin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to 40ab35596e6fcb82a4dfc56e186befb807f8e2af
This commit updates the EE repository reference after PR #873 was merged in windmill-ee-private.
Previous ee-repo-ref: a5def184a29fffab8ed42c70c5a7445b2b3cbe98
New ee-repo-ref: 40ab35596e6fcb82a4dfc56e186befb807f8e2af
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
The proposal reached the deployed page through the tab url's hash, which the
page already reads for "Run again". Two things were wrong with that carrier:
- A tab already open on the item does not remount when it is re-pointed, and
both detail pages read the hash once at init — so a second proposal for the
same item opened its form on the first one's arguments.
- Tab urls are persisted with the session, so a draft holding file bytes or an
un-minted `password` literal was written to IndexedDB. The transcript avoids
both deliberately, through `redactFileArgs` and `MAX_PERSISTED_ARGS_LENGTH`.
The call now carries its own proposal: `PendingRun.proposedArgs` reads the
card's live draft, the page seeds its form from it at init, and the tab's url
names only the call. `ItemViewerView`'s load key includes that name, so a tab
re-pointed to another call builds its form again.
Two more fixes on the same path:
- `adopt` was read off the prop after awaiting job creation, so a call that
settled during the request could be answered with another call's job. The
press now captures the call it was made under.
- An adopted job's arguments and version are the page's, which this tool cannot
read, but the model was told `Ran with arguments: {}`. It is now told the user
ran it on the item's own page, and that the arguments are the page's.
- Adopting hands the carrying tab to the run, so the page no longer navigates
there as well — that left two tabs open on the same job.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: publish the chat sdk as @windmill-labs/chat-sdk
* chore: publish the chat sdk with a dedicated npm token
* chore: publish the chat sdk with npm trusted publishing
* feat: encrypt workspace secrets with AES-256-GCM
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* docs: note the aead gate covers workers only and is one-way
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: skip secrets the upgrade cannot store and clear its guard on panic
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: start aead secret writes one release after read support
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 06610ecc950ed64a2709d7f1aa8c7b49a95fdbae
This commit updates the EE repository reference after PR #869 was merged in windmill-ee-private.
Previous ee-repo-ref: 599eae0034b34c5e0d3d42df37cf18bbe01f9b49
New ee-repo-ref: 06610ecc950ed64a2709d7f1aa8c7b49a95fdbae
Automated by sync-ee-ref workflow.
* feat: widen variable.value to fit AES-GCM ciphertext of the longest legacy secret
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: refuse legacy secrets longer than 15000 chars so every secret can be upgraded
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: cap legacy secrets at 15000 chars with a check constraint so every writer is covered
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
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>
* fix(groups): recompute member roles when an instance group role is cleared
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: restore sqlx cache entry still used by the scim group delete
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: wait for the raw app build before reading its preview dom
* fix: warn the dom and screenshot tools when the raw app build failed
* fix: tell the dom and screenshot tools when the raw app awaits sdk consent
* fix: tell the dom and screenshot tools while the raw app sdk token loads
* feat: support array arguments in snowflake scripts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: cast the parsed snowflake list to array in the guidance
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: parse a json array default of a sql list argument as a list
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: fill raw app ctx inputs on the server, not from the request
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: read declared ctx inputs from the deployed app at run time
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: count the revocation's cache invalidation apart from the deploy's
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: drop the deploy-time cache eviction trigger, rely on the cache ttl
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: run a workflow-as-code task called from inside a task in place
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: hand an async task body an awaitable for a sync nested task
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: mark the task-body scope per nested task body
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: note the task-body behaviour of step and sleep
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: sign OIDC tokens with an external signer and log every mint
* fix: init the signer in sqs_trigger builds and show signer reachability from results
* fix: key the OIDC key rows by source and kid
* chore: bump ee ref and list the oidc-signer subcommand only where it exists
* fix: advertise the actual signing algorithm in OIDC discovery
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: harden the external signer startup, agent workers and failed mints
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: bump ee ref for the stored key listing fix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: list the oidc signing settings page only with an external signer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to 1e538620a9b2f08b044997d032e71d8720ecbd5c
This commit updates the EE repository reference after PR #859 was merged in windmill-ee-private.
Previous ee-repo-ref: 84fa325737df98407c7b9f79c09d6e9378c46147
New ee-repo-ref: 1e538620a9b2f08b044997d032e71d8720ecbd5c
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat: audit declarative instance config writes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep early logs, base url and license plan for declarative writers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: require an observer on every config apply, drop license loading
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: export only audit events from the declarative subcommands
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to d6a6cdd343e7744ea49c610f9534c8013c8ffc2a
This commit updates the EE repository reference after PR #872 was merged in windmill-ee-private.
Previous ee-repo-ref: fc51c31bafb5975694fffe1da51f7b41fbf127a6
New ee-repo-ref: d6a6cdd343e7744ea49c610f9534c8013c8ffc2a
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* test: stop success handler tests sharing the deployed script cache
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: drop the sqlx cache entry of the removed test query
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep ai agent schema property order stable across jobs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep tool parameter order when filtering preset inputs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: make the packaged components resolvable by consumers
The svelte-package output of @windmill-labs/components imported the chat
SDK from outside the package root, pointed `$lib/utils` at a directory
with no index, and shipped an `import.meta.glob` on the `$lib` alias.
Bundle the chat SDK entry point into the package, repair the directory
specifier, make the glob relative, and fail packaging on any import a
consumer cannot resolve.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: pin the chat sdk output root and widen the packaging checks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: resolve $var: references in mcp resource headers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: require variables:read scope to resolve mcp header variables
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: restyle s3 explorer tree like the db manager tree
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: make s3 explorer flush with its drawer
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: show s3 file preview as an inset card
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: redesign s3 explorer file header
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: move s3 csv preview options into the file header band
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: narrow the s3 csv separator select
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: show csv, parquet and json files as tables in the s3 explorer
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: keep the detected s3 csv separator shown in text view
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: drop stale s3 table responses and unfilterable nested columns
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: pin ee ref for the s3 table decimal filter fix
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: pin ee ref for the s3 table decimal scale fix
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: pin ee ref for strict s3 table decimal filter parsing
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: pin ee ref for the s3 decimal exponent bound
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 06610ecc950ed64a2709d7f1aa8c7b49a95fdbae
This commit updates the EE repository reference after PR #869 was merged in windmill-ee-private.
Previous ee-repo-ref: 74f041e9767f2ac612e21f351f96e71c584e2904
New ee-repo-ref: 06610ecc950ed64a2709d7f1aa8c7b49a95fdbae
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>
* fix: parse python 3.12-3.15 syntax by moving the python parsers to ruff
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: pin async for loops in the python wac dag
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: tell main apart from a look-alike line inside a string
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep import split and async for as before, fix sql asset path panic
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore: bump uv to 0.13.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: pin the python ansible is installed on in the full images
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep preserve_step_tags on saved branch and loop flow nodes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* style: format reduce_flow call
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore: upgrade the native runtime to the current deno crates and v8 150
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep data and blob fetches working under no_network
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: pin the simdutf v8 archive and keep telemetry on http exporters
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: force the http otlp exporter for native telemetry and pin v8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: retry transient AI provider failures before output reaches readers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep completed AI streams, stop retrying prompt blocks, bound retry-after
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: share the AI send-error retry rule between agent and proxy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: accept a clean end of stream from OpenAI-compatible providers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: classify AI provider errors the way the reference clients do
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: count a Gemini function call as the end of its turn
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: count an Anthropic stop_reason as the end of its stream
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: accept a Gemini answer that reports usage and no output
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: accept a Gemini stream that closes cleanly with no output
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>