* feat: show the new-tab icon on a chat path pill while the modifier is held
Fixes WIN-2477
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq
* fix: read the new-tab modifier in the capture phase
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq
* refactor: track the new-tab modifier only while a pill is hovered
The window key listeners were installed at import time and never removed, so
every page that loaded the module paid for them whether or not a pill existed.
They now attach on mouseenter and detach on mouseleave or destroy, which is the
only window in which the answer is read.
Seeding the flag from the hover event also removes the limitation the previous
version documented: a mouse event carries the same modifier flags as a key
event, so a modifier held before the pointer arrived, or while this window was
unfocused, now reads correctly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq
* refactor: export the new-tab modifier as a read-only view
`newTabModifier` handed every consumer a writable handle on module-global
state, so any of them could drive the icon of every pill on the page. The
getter form is what frontend/AGENTS.md prescribes for shared reactive state.
Tearing each attachment down in the test's afterEach as well: the module state
and its window listeners outlive the DOM, so emptying the body left `held` and
the hovered node set for the following case.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq
* refactor: only track the modifier for pills whose icon can change
The attachment went on every path pill, so hovering a drawer or plain-link pill
installed three window listeners for a flag its icon never reads. Only a
preview pill can flip, so only it gets them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq
* fix: re-read the new-tab modifier from pointer movement
A modifier held across a keyboard app switch was cleared by the blur and never
restored: the key was down the whole time so no keydown arrived on the way
back, and the pointer parked on the pill fired no fresh mouseenter either. The
pill then showed the panel icon while the click would have opened a tab.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq
* refactor: give each pill its own modifier state
The shared module state forced a node-identity guard: one hovered element owned
the window listeners, so a pill destroyed elsewhere in the transcript had to be
stopped from tearing them down. A factory per pill removes the guard, its test
case, and the whole class of cross-instance interference, and narrows re-renders
to the hovered pill instead of every preview pill on screen.
Listener teardown now goes through AbortController signals, so leaving a pill
drops the whole set at once rather than through a remove list that has to mirror
every option exactly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq
* fix: abort the previous hover controller on re-entry
A second mouseenter with no mouseleave between replaced the controller without
aborting it, so the four listeners registered under the first signal outlived
even the element's destruction: neither leave nor the destroy path held a
reference to reach them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: write and read python job files as utf-8, not the platform locale
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ErBZtEpLpkFi1Y1W6eNZBE
* refactor: trim the PYTHON_UTF8_ENVS comment to the 4-line limit
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ErBZtEpLpkFi1Y1W6eNZBE
* chore: bump ee ref for the python runner-group utf8 companion
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ErBZtEpLpkFi1Y1W6eNZBE
* chore: update ee-repo-ref to d33ea730c550cdbc7d050aeb6d40dcef3d134e07
This commit updates the EE repository reference after PR #782 was merged in windmill-ee-private.
Previous ee-repo-ref: c8318661f8d91da9172a3c2dca050b70ba7afda2
New ee-repo-ref: d33ea730c550cdbc7d050aeb6d40dcef3d134e07
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>
* fix(oauth): show the account chooser on Google/Microsoft login
Without `prompt=select_account`, Google and Microsoft silently reuse the single
active browser session, so a user with more than one account has no way to pick
which one to sign in with.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0196aV8v36ukoQcD7L2scvmH
* chore: pin ee ref for the oauth login extra_params fix
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0196aV8v36ukoQcD7L2scvmH
* fix(oauth): only ask for the account chooser on an explicit login click
The login page now sends `user_initiated=true` when someone clicks a
provider button, and the backend applies the provider's `extra_params`
only for those requests.
Someone whose browser holds a single Google session whose email is
already registered under a different login type hits
"an user with the email associated to this login exists but with a
different login type" and, with no account chooser, has no way to offer
a different account. The chooser belongs on that click.
It does not belong on the `auto_login_provider` redirect, whose whole
purpose is to sign a public-app or approval-page visitor in without
interaction.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0196aV8v36ukoQcD7L2scvmH
* fix(oauth): make the account chooser the default, not the opt-in
The login page now flags only the `auto_login_provider` redirect, with
`auto=true`; every other login — a click on a provider button, or the
endpoint opened as a plain URL — gets the provider's extra params.
`/api/oauth/login/*` is whitelisted in `public_app_layer` and reachable
directly, so an opt-in flag would silently drop the account chooser for
every caller that is not our own button.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0196aV8v36ukoQcD7L2scvmH
* chore: update ee-repo-ref to f5d6b6b8dd00b0141308337ac97f4685781f2b1c
This commit updates the EE repository reference after PR #776 was merged in windmill-ee-private.
Previous ee-repo-ref: 5684bb0f63dce08d6ce9ab0183072c8b4fce4b2e
New ee-repo-ref: f5d6b6b8dd00b0141308337ac97f4685781f2b1c
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>
* fix(frontend): render ordered lists in markdown descriptions
`GfmMarkdown` defaulted to `prose-xs`, which Tailwind Typography does not
define — the class only ever matched four hand-rolled rules in app.css, all
scoped to `ul`. Every surface on that default (script and flow descriptions,
flow-graph notes, markdown job results) therefore rendered `<ol>` with
Preflight's `list-style: none` and no typography at all: no numbers, no
heading or paragraph rhythm.
Route the default through the shared `markdownProse` stacks instead, and cut
the app.css list rules down to the dash glyph so ordered and unordered lists
share Tailwind Typography's indentation and rhythm.
Fixes#10971
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S6G5gDXJnm6uqch4uCPkPE
* fix(frontend): address review nits on the markdown prose fix
- default `GfmMarkdown` to the `sm` stack rather than `xs`: the AI-agent tool
Message pane takes the default and has no ancestor font size, so `xs` left it
smaller than its own label. The group note, whose wrapper is `text-2xs`, opts
down explicitly.
- regenerate `static/tailwind_full.css`, which raw apps are served and which
still carried the deleted list rules.
- correct the marker-color rationale: the typography config already maps markers
to tertiary, so the rule steps them up rather than rescuing them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S6G5gDXJnm6uqch4uCPkPE
* fix(frontend): make the note color override an arbitrary value
`text-inherit` is not generated: this config replaces the Tailwind color palette
outright and defines no `inherit` key, so `[&_*]:!text-inherit` compiled to
nothing and notes still rendered in the prose stack's `text-primary`. Verified in
the browser: a yellow note's list items now compute to `text-yellow-900`, matching
the wrapper and the edit-mode textarea, in both themes.
Also drop the `static/tailwind_full.css` regeneration. That file was generated with
tailwind 3.4.1 against a config predating the typography theme overrides; rebuilding
it today sweeps in 250KB of unrelated churn and would flip every raw app's `.prose`
palette from stock gray to Windmill tokens. Its staleness predates this PR and is
its own change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S6G5gDXJnm6uqch4uCPkPE
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: guest JWT entry for embedded apps (jwt_guest_)
A second way in for a guest, alongside the signed-in guest session: a JWT the
embedding customer's backend mints and signs, verified per request against a
per-workspace key (a PEM public key or a JWKS URL), resolving to the same
seatless guest identity confined to the one app its app_path claim names.
Bearer prefix jwt_guest_, stateless (no token row). See PR #10954.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat: surface guest JWT as the embed method in the app deploy drawer
The deploy drawer explained the secret-URL embed but not the guest JWT path, so
the primary way to embed an app for a customer's own authenticated users was
undiscoverable. For a guest-mode app with guests enabled, show how to mint a
`jwt_guest_` token and append `guest.<jwt>` to the app URL, with a copyable
iframe template pre-filled with this app's workspace_id and app_path, and a note
that new guest emails are refused past the instance's free allowance (the live
count is shown just above).
Also log a guest JWT allowance refusal at warn, not info: the caller gets a bare
401 (the reason must not leak to an unauthenticated caller), so the log is the
admin's signal that the instance hit its guest cap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: correct the guest JWT minting instructions in the embed block
The block said "sign it with the workspace's guest JWT key", but that setting
holds the public verification key. Clarify the keypair relationship (configure
the public key or a JWKS URL in the workspace; sign with the matching private
key), name the accepted algorithms (RS/PS/ES; HS* refused), and keep the
required claims, so an embedder knows how to actually mint the token.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat: fall back to the instance JWT issuer for guest verification (off on cloud)
A workspace with no guest key of its own now verifies guest JWTs against the
instance issuer (JWT_EXT_JWKS_URL, already used by jwt_ext_), so an operator
running one issuer configures it once. Verification and the guest grant are CE;
granting a full login from that issuer stays EE (jwt_ext_, unchanged). Disabled
under CLOUD_HOSTED, where one instance issuer must not be trusted to mint guests
in every tenant's workspace — there the per-workspace key is the only source,
which also stays the override everywhere. The workspace settings note (hidden on
cloud) explains the fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: embed instructions cover both the workspace key and instance issuer
The embed block said to set the workspace's guest JWT key; now it says Windmill
verifies against the workspace key or, off cloud, the instance issuer
(JWT_EXT_JWKS_URL) when no workspace key is set. The instance clause is hidden
under isCloudHosted().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: show the guest JWT embed block only when Embed is toggled
It belongs with the iframe snippet, not the plain-URL view, so gate it on
embedMode alongside the guest-mode / guests-enabled checks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: trust the instance issuer in the guest fallback; refresh stale docs
P1 (CI review): the fallback wrapped JWT_EXT_JWKS_URL as a workspace JwksUrl, so
it hit validate_guest_jwks_url and was refused for http/private issuers unless
ALLOW_PRIVATE_GUEST_JWKS_URLS was also set — a self-hosted internal issuer that
works for jwt_ext_ failed for guests, though the UI says setting the env var is
enough. fetch_jwks now fetches the instance issuer without the https/private
restriction (matching the jwt_ext_ loader; it stays operator-trusted), while a
workspace-admin URL is validated and pinned as before. All the size/key/URL
bounds still apply to both.
P2 (CI review): refresh the stale docs that said a missing workspace key always
refuses a guest JWT — the module, bearer, key-source, and EditGuestJwtKey field
docs now describe the workspace key with the off-cloud instance-issuer fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: fetch the trusted instance issuer like the jwt_ext_ loader
P1 (CI review): the instance-issuer fetch skipped SSRF validation but still
disabled redirects and default cert validation, so an instance issuer that works
for jwt_ext_ through a redirect or an operator-approved self-signed cert failed
the guest fallback. Fetch it with HTTP_CLIENT_PERMISSIVE (follows redirects,
honors ACCEPT_INVALID_CERTS) — the same behavior jwt_ext_ has — while a
workspace-admin URL stays validated, DNS-pinned and redirect-free. The body size
cap still bounds both.
P2 (CI review): the WorkspaceSettings field doc still said None/None means no JWT
guests; it now names the off-cloud instance-issuer fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: schema summary + OpenAPI cover the guest JWT columns and fallback
P2 (CI review): summarized_schema.txt was missing guest_activity.jwt_entry and
the two workspace_settings guest-JWT key columns (required by docs/validation.md
after a schema change). The edit_guest_jwt_key OpenAPI description now notes that
clearing the workspace key falls back to the instance issuer (JWT_EXT_JWKS_URL)
off cloud rather than necessarily stopping guest JWTs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: keep JWKS single-flight locks in a self-cleaning map, not a bounded cache
P1 (CI review): JWKS_FETCH_LOCKS was a 200-entry quick_cache. Past 200 cold URLs
it can evict a lock whose fetch is still in flight; the next request for that URL
then mints a fresh lock and starts a second fetch, so cycling configured
workspaces defeats single-flight and can storm the issuers. Replace it with a
plain map guarded by a JwksFetchLock RAII handle that removes each entry once its
last holder drops, so the map only ever holds the fetches in flight and never
evicts an in-flight lock. Add a unit test pinning the shared-lock and
self-cleaning invariants.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: update ee-repo-ref to c2270eb5fe2d9f0968253e6b460c33186363f4e7
This commit updates the EE repository reference after PR #773 was merged in windmill-ee-private.
Previous ee-repo-ref: 5a1d9dee34159512c0823fddcd3d096490edbcce
New ee-repo-ref: c2270eb5fe2d9f0968253e6b460c33186363f4e7
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: bump the ee ref past the seats_consumed test arity break
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxTTC4GsAANBjEZki2cft
* chore: update ee-repo-ref to fb1c5c109846d6c47aff70ab6cc631f4fd773678
This commit updates the EE repository reference after PR #781 was merged in windmill-ee-private.
Previous ee-repo-ref: d197b7b1c76e2aa7cde6cef2e2d9556607cce4c6
New ee-repo-ref: fb1c5c109846d6c47aff70ab6cc631f4fd773678
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>
* feat(frontend): group the AI agent step form and edit saved agents in a modal
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: edit a saved AI agent through its own resource draft
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: drop the agent fork-for-edit session now that edits live in a draft
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: edit ai_agent resources from the resources page with the agent editor
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: send a standalone agent's brain from the module when testing a step
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: keep the agent draft faithful to the resource it deploys to
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore: add the sqlx cache entry for the eval subject rename
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: share the module insert between the graph and the agent editor
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): open evals inside the agent editor, actions in its header
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): add tools from the agent editor and lighten its test pane
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): open an ai_agent deep link in the agent editor
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): drop the failed result badge on a step that never ran
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): head the agent editor's levels with a back control
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): drop connect and fill inputs from the agent editor
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): lighten the agent editor's run panel
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): stop a nested agent tool's config reading as AI-filled
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): offer only AI or static on an agent tool's inputs
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): give a saved agent's tool editor a static-only surface
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): open an agent tool in a drawer beside the agent
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): hide unset agent config in the run form
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(frontend): share the input forms' pickers and s3 lookup
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(frontend): drop a dead agent-editor export and fix two stale comments
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): reach an ai_agent's resource-level settings and copilot
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): open an ai_agent's resource view as JSON, not the generic form
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): address review findings on the agent editor's draft and streaming
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): close the agent editor on a version restore, as the resource editor does
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): stop the provider picker auto-writing a kind, and clear review nits
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(frontend): drop the fork-for-edit leftovers from the agent card
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): mount the agent editor in the dev flow editor and guard the deep-link race
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): deploy the agent config that was submitted, and refuse one no run could use
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(frontend): build the agent editor's rows from the design-system button
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): keep a draft-only agent's draft, and let a blank MCP summary deploy
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): guard read-only agents, incomplete MCP tools and duplicate editor mounts
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: read-only agent editor, linked-card refresh, atomic eval rename
* fix: eval rename needs the privileged pool, per-workspace write access
* fix(frontend): drop the agent editor target when its mount goes away
* refactor: drop the agent rename work from this PR, unban the bindable defaults
* fix(frontend): refuse a renaming deploy and drop the copilot from static-only fields
* fix(frontend): mirror the worker's streaming rule and scope agent writes to their target
* fix(frontend): read runtime streaming as off and reset the drawer's json view
* fix(frontend): read an unsettled output_type as non-streaming too
* fix(frontend): let the showing modal claim an agent opened from inside it
* fix(frontend): keep in-flight edits, tool replacements and every linked step in sync
* fix(frontend): keep attachments in the run form and bind the agent ref to its tools
* fix(frontend): preview the agent as authored and re-evaluate step args on run
* fix(frontend): scope agent-editor ownership to the flow's workspace
* fix(frontend): drop the tool drill-in where there is no graph to select on
* fix(frontend): require a provider kind and keep one resource editor open at a time
* fix(frontend): keep legacy nulls, static-only text literal, and the handover anchor
* test(worker): pin the agent streaming default
* fix(frontend): let an AI-fillable input be switched to static
* fix(frontend): report agent editor background failures instead of floating them
* fix(frontend): keep the version pane's path alive while the editor closes
* fix(frontend): clear the anchor-keep flag at the start of each drawer session
* fix(frontend): preview the agent without its synthetic path, refresh the baseline on external writes
* refactor(frontend): drop the unverifiable baseline refresh, state the synthetic-path rule
* fix(frontend): keep the synthetic path out of agent tool test runs too
* refactor(frontend): host the agent editor under the agent's own path
* fix(frontend): mark an agent editor's host explicitly instead of inferring it from the path
* fix(frontend): discard linked-agent responses from before a deploy
* fix(frontend): keep a flow mount from claiming an agent editor's nested target
* feat(frontend): keep an agent used as a tool inside the agent being edited
* fix(frontend): reserve the agent editor's root module id
* docs(frontend): record why the agent editor previews under the agent's path
* fix(frontend): refuse to open or deploy a resource that is not an agent
* docs(frontend): put the scope-migration comment on the function it describes
* fix(frontend): refuse an agent path whose resource type is not proven
* fix(frontend): recheck the resource type before deploying, and keep expressions off static-only inputs
* fix(frontend): lazy-load the agent editor and slide its levels like the evals pane
* refactor: drop unreachable non-list tools check from agent deploy
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): clear text-only agent fields on image output, reserve the root id
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): keep the agent editor usable for a non-list tools value
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): stop the parked eval run list from taking arrow keys
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): report a non-list tools value on deploy instead of throwing
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): keep temperature editable for image output
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): skip non-object tool entries when rendering an agent
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): guard tool entry reads instead of copying the tool array
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): key tool rows by position so duplicate ids render
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>