Commit Graph
40 Commits
Author SHA1 Message Date
GuilhemandClaude Opus 5 449b1a6933 fix: ground the chat's AI agent provider in the workspace's models (#10774)
* fix: ground the chat's AI agent provider in the workspace's models

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

* fix: never reject an AI agent model the catalog could not confirm

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

* test: benchmark AI agent provider grounding in ai_evals

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

* fix: only reject an AI agent model an exhaustive listing rules out

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

* fix: keep instance-level AI settings out of the workspace provider catalog

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

* fix: keep untrusted model ids out of the chat's context

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

* refactor: carry completeness on the model listing itself

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

* docs: correct two comments left behind by the catalog rework

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

* fix: bound the model listing and verify the default against it

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

* fix: honour a workspace default a filtered listing names

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

* fix: recognise a workspace default past the prompt's model cap

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

* fix: keep an aliasing provider's unlisted model ids permissive

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 10:42:02 +02:00
574775d50c fix: teach the AI the raw-app job bindings, the SDK reference and the draft/deployed split (#10754)
* feat: teach the AI the raw-app job bindings and the draft/deployed split

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

* fix: scope the raw-app deploy advice to the referenced item, and stop kind-conversion from stranding fields

The draft/deployed guidance added in the previous commit was read as "deploy the
app too": the agent asked for both the flow and the app and routed a one-item
dependency through the review-and-deploy page. Only the referenced flow or
script has to exist deployed — the preview runs the app's draft — so the prompts,
the `write_app_runnable` warning and the testing rule now say to offer that one
deploy and leave the app a draft.

`buildPersistedRunnable` spread the existing runnable when rewriting it, so
converting a path runnable to inline left `runType`/`path` behind (and the
reverse left `inlineScript`). `isRunnableByName` matches the inline branch
first, so an app "wired to a flow" silently ran stale inline code.

`test_run_app_runnable` now fills ctx-bound inputs with `$ctx:<prop>` the way
RawAppBackgroundRunner does, so a ctx argument no longer arrives missing.

The SDK-reference rationale claimed WM_TOKEN may be unset, that a missing base
URL falls back to localhost, and that a job token is scoped enough to 403 a
hand-rolled REST call. None of the three is true, and it shipped to every
write-script prompt; the text now only says the client configures itself.

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

* fix: address review round on the raw-app AI instructions

The eval case could pass on the exact answer it exists to reject. Every
`requiredMentionsAnyOf` alternative but one was flow-agnostic, so "the app must
be deployed" satisfied "must be deployed". All alternatives now name the flow,
and a unit test pins that the app-only phrasing fails.

`instanceLine` asserted "self-hosted Community Edition" outside the browser,
where `isCloudHosted()` reads false and the license store is unset — so every
global eval was told that regardless of what it pointed at. It is now emitted
only under BROWSER.

`assistantExpect.forbiddenMentions` defaulted a missing `assistantText` to "",
which passes every entry forever on a mode whose runner does not report it.
It now fails with that as the reason.

`buildPersistedRunnable` carried `schema` across a retarget, so a path runnable
pointed at a new flow kept the previous item's schema and `genWmillTs` typed
`backend.<key>(args)` from the wrong inputs. It survives only while kind and
path both match.

The SDK header claimed "a function that is not listed below does not exist".
`windmill-client` also exports the generated services, and the Python client
exposes `Windmill.get`/`.post`, so an endpoint without a helper had no legal
move. Each language now names its own escape hatch.

`getAppInstructions` said the attached reference carries the TypeScript SDK even
when `language: "python3"` had swapped in the Python one — on the very sentence
telling the model to make that call.

The kind-conversion comment claimed a hybrid runnable "silently runs stale
inline code". It does not: `isRunnableByName`, `isRunnableByPath`,
`convertPersistedToBackendRunnable` and `rawAppPolicy.processRunnable` all
dispatch on `type` alone. The leftovers contradict the runnable's kind rather
than override it, which is what the comment now says.

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

* fix: address round-2 review nits on the raw-app AI instructions

`flow is deployed` was satisfied both by "once the flow is deployed, the button
works" and by a hallucinated "done — the flow is deployed", which eval mode makes
impossible and the drafts-only judge cannot see. Every alternative now states an
outstanding obligation, and two more real phrasings ("will need to be deployed")
are accepted so a correct answer is not failed on wording.

Condenses the three comment blocks that ran past the four-line limit in
AGENTS.md, and drops two claims inside them that no longer hold: the
`testRunAppRunnable` doc said it runs a runnable the way the app's own frontend
does (it is the editor preview, which a deployed app's stored policy does not
match), and `undeployedRunnableTargets` described its argument as the write
tool's raw input when the call site passes the persisted runnable.

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

* fix: report the real cause when a test run fails, and label the app-runnable card

Driving `test_run_app_runnable` in a live session surfaced two defects the
API-level check could not see.

`executeTestRun` built its failure message from `error.message`, which the
generated client leaves as the bare status text while the server's message sits
in `body`. A path runnable aimed at an undeployed flow reported "Not Found"
instead of "Not found: flow not found at name u/admin/current_time" — dropping
the one diagnostic the run exists to produce. `formatToolError`, in the same
file and written for exactly this, now does it. This also applies to
test_run_script and test_run_flow, which had the same loss.

The completion card read "Flow test completed successfully" for an app runnable,
because `contextName` doubles as the jobs-tray kind and a path runnable pointing
at a flow really does queue a flow job. A `completionName` override now names
what ran without changing the kind.

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

* test: pin the deploy expectation against wrong answers, not just correct ones

`deploying the flow` was satisfied by "done deploying the flow" — a deploy the
agent only claims to have made, which eval mode makes impossible and the
drafts-only judge cannot see. Replaced with the prospective forms, and dropped
the same reading from the workflow variant.

Three review rounds each found this same class of hole in the phrasing list, so
the list is now exercised against the wrong answers themselves rather than
eyeballed: naming the app as what needs deploying, claiming the deploy is
already done, claiming to have deployed the flow, and saying nothing about
deploying all have to fail, while four real correct phrasings have to pass. The
test reads the case out of global.yaml, so a future edit to the alternatives is
checked by it.

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

* test: drop the tense-neutral deploy alternatives and cover completed claims

A gerund after a preposition carries no tense, so `before`/`after`/`by deploying
the flow` all match a deploy the agent only claims to have made ("after
deploying the flow, I clicked the button and it returns the greeting") just as
the bare gerund did. All three are gone rather than swapped for whichever reads
least badly, and the two completed-deploy phrasings are now negative fixtures.
The remaining alternatives are imperative or obligational, which a claim of
having already deployed cannot satisfy.

Condenses the two comments this list carries: the YAML block to four lines, and
the test's rationale to the durable constraint about substring matching.

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

* fix: encrypt sensitive inputs when test-running an app runnable

`test_run_app_runnable` sent `force_viewer_static_fields` but not
`force_viewer_sensitive_inputs`, which every other preview path derives from
the runnable's `sensitive` user fields. That list is the only thing driving the
encryption loop in apps.rs, so testing a runnable with a sensitive input wrote
the real value into the job's args in plaintext, readable by anyone with run
access to the workspace.

Verified against a running EE instance. With the list, `api_key` is stored as
`$encrypted:mvqtSRI9…` and the sentinel appears nowhere in the job record;
without it, the sentinel is readable in run details. A non-sensitive field is
left plaintext either way.

The tool claims parity with the editor preview, so it uses that same filter
(`type == 'user' && sensitive`) and omits the field entirely when empty.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-08-20 11:17:33 +02:00
AlexRV12 fd9295a58e feat(copilot): let plan mode draw, but never write the plan (#10725)
* fix(copilot): validate the version an approval stamps

* feat(copilot): let plan mode write artifacts, but never the plan

* feat(copilot): tell plan mode it may keep notes, not rewrite the plan
2026-08-17 21:17:10 +02:00
AlexRV12andClaude Opus 5 850b028778 feat: advertise the pinned artifact version in get_preview_status (#10691)
* test: let global evals seed the session's preview tabs

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

* feat: advertise the pinned artifact version in get_preview_status

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

* test: reject ambiguous preview-tab and artifact eval fixtures

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 18:41:09 +02:00
AlexRV12andClaude Opus 5 caa189868c feat(ai-sessions): add plan mode (#10057)
* feat(sessions): let an opener name the artifact version to show

A tab already remembers the version a reader pinned, and re-pointing it keeps
that pin. Plan mode needs the two intents that leaves out: a plan card scrolled
up the transcript wants the version it proposed, and a plan going up for
approval wants the current text with no pin at all.

`ArtifactVersionTarget` is those two alongside the existing one: a number,
`'latest'`, or omitted. Omitted still cannot double as `'latest'` — every
artifact tool re-opens the document it just wrote, so taking that as a request
to move would yank a reader out of the version they chose on every edit the
agent makes.

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

* feat(copilot): add the plan-mode gate and tag plan-mode-safe tools

Plan mode is a read-only posture, so something has to decide which tools it
may still run. `Tool.planModeSafe` is that tag, and processToolCall fails
closed on it: untagged means mutating means blocked. Deriving it from
`requiresConfirmation` was not an option — unconfirmed mutating tools exist,
and a posture that leaks one is not a posture.

The gate runs twice per call. Before `validateBeforeConfirmation`, so a
validator cannot reach out while planning; and again after the confirmation
wait, because plan mode can be entered while a mutating tool's card is
already pending, and that approval must not carry it through.

Arguments are read one field at a time rather than through a parse of the
whole call. `change_note` is optional and cosmetic, and a model that sends it
as `null` would otherwise fail the object parse and take the plan down with
it — the user being told there was no plan to approve, which is false.

Also here, because refusing a call well needs them: a validator may now
return the row the user reads and the result the model gets separately, a
tool may word its own cancellation, and a tool may start work when its card
appears rather than when it is approved. The gate is consulted before any of
them.

`shouldAutoAcceptToolConfirmations` is asked about the tool by name, because
skipping the confirmation wait is itself an answer on the user's behalf and
one tool must not be answered for. Deciding that without the name would put
the exception out of reach of the only path that needs it.

The gate stays inert until a chat supplies `isPlanModeActive`.

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

* feat(copilot): give a session one versioned plan document

The plan the user agrees to has to survive `/clear`, so it belongs to the
session rather than the conversation, and a session holds exactly one. Its id
is the session's, so the primary key is the constraint — there is no second
row to mint, no index to maintain and no schema change at all.

Every write reads the row it is about to replace inside the transaction that
replaces it. Read outside, two tabs both see version N, both stamp N+1, and
the later write silently drops the earlier one's text and its snapshot;
IndexedDB serialises readwrite transactions over a store, so read and write
together cannot interleave. Approval takes the same route but patches only
the pointer: an approval computed while another tab was revising must not
carry this tab's older content back over the newer text.

Approval is `approvedVersion`, a pointer at a version, never a flag. Below
the current version means the newest text is a proposal the user has not
agreed to; absent means nothing here was ever approved. Only exit_plan_mode
can leave the pointer behind, since every write outside plan mode carries it
forward — an amendment the user's posture already trusts is still the agreed
plan. Declining writes nothing at all: the refused proposal stands as the
newest version, with the agreed one still in history.

Nor can create_artifact confer approval. It asks for no confirmation, so the
model writing a plan document is not the user agreeing to one; a plan written
there holds the session's slot as a draft until a decision lands on it.

That is also why the approved version is exempt from pruning. A plan approved
at v1 and then planned against for twenty more rounds would otherwise lose
the very version that stands as agreed, and with it the card that opens it,
the banner offering it back, and read_artifact at that version. It is
excluded from the pruning candidates rather than added on top, so the budget
is unchanged and what survives simply stops being contiguous.

The write reports whether the database took it. Most callers still degrade
like the reads do, but a plan cannot: returning one the database refused
would let the user approve and execute against a document that disappears on
reload — a refused plan write raises instead.

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

* feat(copilot): add plan mode — the posture and its two tools

enter_plan_mode asks to hold work; exit_plan_mode hands over a plan and,
on approval, gives the posture back to whatever preceded it. Both carry
`planModeSafe`, since a posture with no exit is a trap. Only the transition
the current posture allows is offered, so there is no tool for leaving a
posture the chat is not in.

A planning round runs from entering plan mode to the proposal the user
decides on. It remembers only the write it made, because nothing it does is
undone — and that write is shared between the card's confirmation hook and
the tool's `fn`, so the plan is on screen while the user is deciding whether
to approve it rather than after.

The round is identified by an epoch bumped on *entering*, not by the
conversation. A chat rotation mid-approval must still let that approval hand
the posture back; a round the user has since left and re-entered must not,
or approving the old plan would drop them out of a read-only posture they
just chose.

Saving a proposal revises the session's plan document and creates one only
when there is none — both halves in a single transaction, so a second tab
proposing at the same moment revises the row this one wrote rather than
racing it.

Persistence failures hold the posture. Approval is reported only once both
the proposal and the approval pointer are durable, so a plan the database
refused cannot unblock mutating tools. The failure is reported from `fn`
and no earlier: the write settles while the card is still waiting to be
confirmed, and clearing that card from underneath the wait would take away
the only control that resolves it.

An auto-accepting posture answers for the user through one predicate, asked
by every path that answers: the pending-card sweep, the confirmation itself,
and the decision to skip the wait at all. enter_plan_mode never qualifies:
YOLO means "stop asking and run it", and a call from a tool set snapshotted
before the switch must not answer that with a read-only posture — whether its
card is already pending or has yet to be registered.

Plan mode lives in its own controller with a narrow view of the chat it runs
in: it reads that autonomy state and asks for the two changes it can cause,
rather than owning any of it.

Plan mode is offered only in a session chat, and a session chat is GLOBAL for
its whole life. The gate reads that mode, so `changeMode` refuses to move one
out of GLOBAL rather than resting the invariant on a picker being hidden.

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

* feat(copilot): surface plan mode in the chat and the artifact list

Plan mode is the only posture that refuses work, so the composer says so
before the user types the request it is about to turn down: the mode pill is
tinted whole rather than by its icon, and the empty placeholder carries the
constraint in words. Teal, not the house green — green is the transcript's
success colour a few rows up, and a mode signal in it would read as "this
worked" rather than "this is held".

A blocked tool renders as its own lean row naming the tool, not as an error:
the call did what plan mode says it should, and "why can't it edit" is
answered where it is asked.

A plan card names the decision — proposed, approved, or not approved — and
never the button, since a Stop and a posture switch resolve it too. Its
button opens the version that card proposed, so a card far up the transcript
still shows the plan it put forward rather than whatever the document has
become since.

The artifact list and the preview header both label the plan through one
badge helper, so the two cannot disagree about what counts as one: a plan the
user never approved keeps the plan icon and takes the neutral badge, leaving
the teal to mean exactly one thing. In the viewer, an unapproved revision
says so in a bar that cannot be scrolled past, with the version the user did
agree to one click away.

The autonomy picker became a table with one row per posture, so adding one
touches a single place instead of four parallel switch statements.

A version of a plan is read against the one the user approved, not against the newest:
latest is only where the model happened to stop. So the approved version is never stale —
its bar is teal and points forward to the draft rather than warning about it — the version
in front of it is the draft, and anything behind it is history that is neither and takes no
pill at all. The list opens a plan at the approved version for the same reason, which is
what lets its pill say `plan` while an unapproved draft sits at the head.

One helper answers all of it, so the list and the preview header cannot drift apart on what
counts as the plan.

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

* test(ai-evals): exercise plan mode end to end

A case a unit test cannot stand in for: it starts in plan mode against the
real gate and the real exit_plan_mode, and grades whether the model
researches and hands over a usable plan instead of guessing at one.

The checklist does not grade what the harness does for the model —
exit_plan_mode writes the plan document itself, so "saves the plan as an
artifact" would pass on any run where the tool is called at all.

The eval store seeds artifacts with history and mirrors the store's own
approval rules, so a rename cannot promote a proposal the user turned down.

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

* fix(ai-evals): import the plan-mode messages from the module that owns them

`PLAN_MODE_MESSAGES` moved to `planModeMessages.ts`; `planMode.ts` imports it
without re-exporting. Under vitest, which runs the frontend adapters, the stale
import resolved to `undefined` rather than failing to link, so
`global-planmode1-hands-over-a-plan` threw on the approval message after the
posture had already been dropped and the tool withdrawn.

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

* fix(copilot): state plan mode's constraint in neutral text

The composer's two-tone placeholder becomes a plain "Read-only" beside the
autonomy picker, next to where YOLO puts its own warning, and a blocked call's
row drops the mode colour. Teal is left marking what the posture is — the
badge, the version bars, the pill — rather than every call it refuses.

ContextTextarea goes back to main with the accent: `placeholderAccent` had no
other consumer, and the aria-label existed only because the accent blanked the
native placeholder.

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

* fix(copilot): hold the plan header's verdict until the snapshot lands

Opening a plan at the version its reader approved pins a version behind the
head, and until that read resolves `shownVersion` is still the head — so the
header wore the draft's badge and its orange "not approved" bar over the very
case the pin exists to serve, then flipped.

The header now says nothing while `restoringPin`, as the body already does.
Judging `pinned` instead would print the approved signal over text that is
still the draft, trading a true transient signal for a false one.

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

* fix(copilot): refuse a hand-over once plan mode has ended

A response can carry two exit_plan_mode calls, and the tool list they run
against is snapshotted before the first one restores the posture. The second
then found the tool with plan mode already over: under YOLO every confirmation
is answered for the user, so it wrote its own summary and stamped the user's
approval on a plan no card had shown them.

Refused in `validateBeforeConfirmation` rather than in `fn`, since
`onConfirmationRequested` writes the document too. The maintenance path is
untouched — a plan still gets revised outside the posture with update_artifact,
which is what the tool's own description already tells the model to use.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 14:27:38 +02:00
GuilhemandClaude Opus 5 eb238e3f0b fix: stop the AI chat destroying secret variables on edit (#10616)
* fix: stop the AI chat destroying secret variables on edit

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

* fix: clear stale staged secret values and state the draft-staging rule

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

* docs: condense the pending-secret invariant to its field

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

* fix: refuse empty and oauth-managed secret values, keep drawer-staged ones in the draft

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

* fix: resolve a variable deploy's secret from one draft snapshot

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

* refactor: make the variable draft the single source of a staged secret

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

* docs: drop stale in-memory secret invariants from comments and the eval

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

* fix: stop null account/expires_at leaking into variable drafts and diffs

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

* fix: report when a variable deploy leaves the secret value unchanged

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

* docs: scope the variable-value readability claims to the chat

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

* docs: correct the secret-draft invariant in the diff masking comment

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

* docs: record why a non-secret value is resent on a partial update

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

* fix: stop "Load secret value" discarding a staged secret

The audit-logged load writes the deployed secret into the draft row the
variable drawer shares with the AI chat, so offering it while that row
already stages a value silently replaces it — and the deploy that follows
carries the old value with no sign the staged one was lost.

The gate that hid the action already existed but keyed on
`isEncryptedDraftValue`, which only holds once a draft has round-tripped
through the server. A value staged in the same tab is still plaintext, so
it slipped through. Key on "anything staged" instead; clearing stays
explicit via Reset.

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

* fix: extend the variable draft's empty-value sentinel past secrets

Two gaps in the chat's variable write path, both from treating "the draft
cannot carry this value" as meaning only "the value is secret".

`variableToDraftState` drops the value of an OAuth-managed variable so a
refreshed live token is never pinned into a draft, leaving '' behind. The
deploy body resent that '' verbatim for a non-secret one, wiping the token
the refresh flow owns. The sentinel now covers every value the draft is not
allowed to hold, which also removes the divergence from
`VariableEditor.save` and the shared deployer.

Making a variable secret when it holds no value produced a secret draft
staging '', a deploy body with no `value`, and the backend's "cannot change
is_secret without updating value too" — the sibling create path already
answers that case with guidance, so answer it here too.

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

* fix: gate the Secret toggle's secret load on the staged value too

The toggle calls `onLoadSecret` on every change so an is_secret flip has a
value to send, but that load overwrites the shared draft row — the same
discard the button gate just closed, reached by a different control.

It now loads only when the row stages nothing, which is exactly when the
flip needs a value fetched. With a value already staged there is one to
send, and it is the one the user or the chat put there.

Blocking the load costs the side effect that used to mask a worse bug: for
a deployed variable, the load replaced an `$encrypted:` marker with real
plaintext before save. Without it, un-securing a marker would store the
marker string as the value, since the deploy endpoints only decrypt it while
is_secret stays true. So the toggle is disabled outright while a marker is
staged — Reset first. That closes the marker case for draft-only variables
as well, where no load could ever have masked it.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 13:13:41 +02:00
AlexRV12andClaude Opus 5 77adf85ccd feat: version history for session artifacts (#10574)
* fix: never replace an in-flight indexeddb open, only a settled one

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

* feat: keep a version history for session artifacts

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

* feat: let the assistant browse an artifact's earlier versions

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

* feat: pick an older artifact version from the preview panel

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

* test(ai_evals): cover the change note the assistant writes on each edit

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

* fix: bound every indexeddb open, not only one told it is blocked

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 19:51:29 +02:00
Ruben FiszelandClaude Opus 5 505705fd3d serve getJob in the ai evals benchmark api catalog (#10511)
* fix: serve getJob in the ai evals benchmark api catalog

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

* chore: scope the frontend format hook to the frontend dir

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

* fix: answer the run-by-path endpoints and mirror the real getJob entry

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

* fix: gate the format hook on a repo-root frontend, not the project dir

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 14:51:48 +00:00
GuilhemandClaude Opus 5 5feec9b4cd fix(ai-chat): make hub script paths readable from the global chat (#10381)
* fix(ai-chat): make hub script paths readable from the global chat

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

* fix(ai-evals): match hub fixtures on whole words, not substrings

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 14:35:01 +02:00
GuilhemandClaude Fable 5 9739d5a2c2 feat: unified read-only diff chat tool (drafts, fork vs parent, search) (#10211)
* feat(frontend): unified `diff` chat tool with cached snapshot, fork mode, and search

One read-only global-chat tool for every comparison: drafts vs deployed
(workspace index + per-item unified patches over stable YAML), deployed
fork vs parent workspace (against="parent_workspace", sharing the fork
banner's compareWorkspaces fetch through a single-flight store), and a
literal grep over changed diff lines. Multi-file raw apps split into
per-file text patches with folder-style index children and per-file
reads. Patches are materialized once into a per-workspace cache keyed on
draft created_at / comparison ahead-behind markers and the workspace
drafts version, so repeated queries never refetch unchanged content.

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

* fix(ai_evals): teach the mock draft backend what the diff tool reads

The diff tool reads drafts through the get_draft overlay, the drafts
listing's draft_only flag, and per-row created_at change markers — none
of which the benchmark mock modelled (fixed timestamp, always
draft_only, overlay ignored), so in evals every draft looked absent and
the model looped to max turns. Mirror production: monotonic
deterministic created_at bumped per upsert, draft_only computed against
the deployed stores, and draft/no_deployed overlays on script/flow/app
reads (404-shaped not-found). Also drop the diff case's judge items
about conversation content the judge never sees — tool usage is already
enforced deterministically.

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

* fix(ai): diff tool reads unsaved editor state instead of going stale

The pre-diff flush honors the auto-save toggle (a read-only tool must
not persist parked edits), which left a gap: with auto-save off — or
after a failed save — the persisted draft the diff reads is stale, and
a brand-new editor-only draft looks absent. Item reads now detect
unflushed parked edits (hasUnsavedDisabledChanges / failed save state)
and diff the in-memory editor value directly, bypassing the snapshot
cache (it must only hold persisted state) with an explicit unsaved-
changes note; index and search modes warn which items' unsaved edits
they exclude. Local values are canonicalized onto the persisted draft
shape so they never diff noisily against the deployed side.

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

* feat(ai): invalidate diff cache the moment any draft write lands

The snapshot cache leaned on time windows (5s listing throttle, 15s
read reuse) to notice writes it didn't trigger itself — an editor
autosave landing between two diff reads could serve the pre-edit patch.
The syncer now exposes onAnySaved (fires for landed upserts AND
deletes, all keys), and the snapshot subscribes once: a landed write
marks exactly that item's patch stale and expires the listing throttle,
so the next read refetches regardless of any reuse window.

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

* fix(ai): reject the diff file arg on single-document items

Passing file for a script/flow/classic-app diff was silently ignored
and returned the whole patch — an explicit error steers the model to
call again without it. Also declares the file arg on the item handlers'
signatures it was already flowing through.

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

* fix(ai): surface empty-file additions/deletions in app diffs

An empty file appearing or disappearing produces no text patch, so the
per-file split dropped it — a draft whose only change was such a file
read "unchanged". Presence changes now keep their added/deleted entry
(patch '', 0 lines), render as "(empty file)" in summaries, and a file
read states the presence change instead of an empty window.

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

* fix(ai): include classic-app drafts in the diff index and fix ++/-- search

itemTypeForKind now maps classic `app` draft rows to the chat app type
(mirroring the read path, which already pairs app/raw_app), so their
diffs materialize in the index and search instead of reporting "not
addressable". Changed-line search is hunk-aware: `---`/`+++` file
labels only occur before the first @@ marker, so a changed source line
like `++counter` (rendered `+++counter`) now matches instead of being
mistaken for a label.

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

* fix(ai): mask every variable value in chat diffs; compare classic apps value-to-value

Variable VALUES never reach a tool result — the chat-wide invariant
read_workspace_item enforces, not just for secrets. Draft-mode diffs
mask both sides with a placeholder pair that still marks WHETHER the
value changed; fork-mode masks at fetch (and still never decrypts);
item reads carry an explicit note. The former secret-only flag is now
valueMasked.

Classic-app drafts hold the bare grid value while the deployed row
nests it beside summary/policy — diffed raw, a one-field edit read as
a whole-document rewrite. Both sides now reduce to { value } via
classicAppDraftValue (pure, unwraps legacy wrapped drafts), which also
cleans the CompareDrafts drawer for classic apps.

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

* fix(ai): honest secret-draft reporting, classic-app metadata split, glob-safe file subjects

A secret variable's sides are both masked upstream, so an empty patch
cannot prove the value is unchanged — such drafts now report
"cannot be compared; may differ" (valueUncomparable) instead of
"matches deployed", in the index and item reads.

Classic-app drafts mirror summary/draft_path into the bare grid while
the deployed row keeps summary as a column: sides now reduce to
{summary, value} via classicAppDraftParts, applied to both sides, so a
summary edit diffs as one and draft-only markers never pollute the
grid diff.

Raw-app search subjects strip the file key's leading slash so
slash-anchored globs like f/x/*.tsx match.

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

* fix(ai): classic-app local edits, comparison-relevant fork fields, conflicts as unflushed

The chat app type spans two draft kinds: item mode now flushes and
probes both raw_app and classic app keys, and the flush sweep includes
classic-app editor cells (kept out of GLOBAL_DRAFT_KINDS so
clearGlobalDrafts never clears an open classic editor).

Fork projections gain the fields the backend comparison counts that
getItemValue drops: flow schema (with a taxonomy-agnostic inline-hash
strip) and resource-type description/format_extension/is_fileset.
Folder display_name is not exposed by the API's Folder type, so it
cannot be projected.

A conflicted save leaves its payload parked with state 'none', so
index/search now count conflicts among unflushed paths and say so in
their warnings.

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

* fix(ai): staged app renames diff as path; flush classic-app cells at their real keys

A staged rename (draft_path) changes where deploy lands an app, so both
app kinds now compare `path` on both sides — a rename-only draft diffs
instead of reading "matches deployed". classicAppDraftParts returns the
staged path separately from the grid.

Item mode resolves each draft kind's own storage path and additionally
asks the listing which row owns a friendly/renamed path — a renamed
classic app's cell lives at its ORIGINAL storage path, which only the
listing knows — so pending/failed/auto-save-off edits are flushed and
probed at the real keys. The appDiffSides rationale comment is
compressed to the repo's four-line limit.

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

* fix(ai): never claim folder parity the API cannot prove

folder.display_name exists only as a DB column — no folder endpoint
returns it — so an identical projection cannot prove a fork folder
matches its parent. Fork index and item reads for folders now say the
display name is not exposed and may be what differs, instead of
"content matches parent". Exposing the field on getFolder is a backend
follow-up.

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

* fix(ai): gap-free patch pagination; forced-fresh comparisons never join older fetches

When the char backstop cut inside a patch window, the continuation
offset still pointed past the requested window — silently skipping the
undelivered lines forever. windowPatch now cuts at the last complete
line and continues exactly there (a single over-budget line is
delivered truncated and stepped past so pagination always advances).

fetchWorkspaceComparison treats an in-flight request as being as old
as its start: maxAgeMs now gates joining it, so a freshness-forced
post-mutation read (maxAgeMs 0) always issues its own fetch instead of
adopting a tally that began before the mutation, and a superseded
request can no longer clobber a newer cached result.

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

* fix(ai): generation-ordered comparison writes; path-only fork reads for enum-less kinds

Concurrent comparison requests can share a Date.now() value, letting a
superseded request's late result overwrite a newer one and be reused
for 30s — cache writes are now ordered by a monotonic request
generation. Test pins the same-millisecond race with the newer request
resolving first.

Fork comparison kinds outside the chat type enum (folder,
resource_type, …) were listed and even advertised as readable but no
call could reach them: a fork item read without `type` is now a
path-only wildcard (ambiguous paths list their kinds and ask for
type), messages label entries by their comparison kind, and pending
index lines for enum-less kinds advertise the path-only read.

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

* fix(ai): grid-based wrapper detection, comparison invalidation on mutations, multi-kind wildcard reads, honest hidden-diff summaries

The classic-app wrapper heuristic keyed on metadata keys the editor
mirrors into every bare grid — a grid with a component named `value`
was reduced to that component. `grid` presence is the discriminator: a
bare App always has it, a legacy wrapper never does.

invalidateWorkspaceDrafts now also drops cached fork comparisons for
the workspace, so the FIRST post-deploy fork read cannot reuse a
banner-prewarmed pre-deploy tally (the snapshot-baseline check only
covered subsequent reads).

Wildcard fork reads return a section per matching kind instead of an
unactionable "pass type" for kinds the chat type enum cannot name, and
the fork index never summarizes ACL-hidden differences as parity —
hidden counts stay directional (a conflicted item counts in both).

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

* fix(ai): address cubic review batch — invalidation scope, races, edge output

Comparison cache: invalidation matches either side of the pair (a
parent deploy moves its forks' tallies), fences in-flight requests
(no new joins, late results rejected via a per-key generation floor),
and the map is LRU-capped. Eviction moves from every drafts-version
bump to deploy success only — draft saves never move the deployed
tally. Fork snapshots also baseline the PARENT's drafts version.

Draft materialization carries a stale-generation token so a save
landing mid-fetch discards that run's pre-save result instead of
repopulating the invalidated entry; a save/delete also expires the
fork cache's hasLocalDraft join. onAnySaved listeners are
error-isolated (a throwing listener must not mark a committed save
failed) and the pagehide keepalive flush notifies them on dispatch.

Output edges: folder fork lines drop the empty parenthetical, and a
patch-window offset past the end reports itself instead of an
impossible range. The eval case pins the diff call's path argument.

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

* refactor(ai): one fencing primitive per cache instead of per-surface races

Review rounds kept finding pairwise races between async producers and
invalidation — each patched with its own fence. Replace the class:

- diffSnapshot: a per-workspace mutation epoch, bumped by every
  invalidation. Both reconcilers run a bounded retry loop — joiners
  re-validate after awaiting, producers refuse to store results whose
  inputs predate a mutation. Covers in-flight listing adoption and
  pre-deploy fork tallies in one mechanism.
- workspaceComparison: per-WORKSPACE generation floors (either side of
  a pair). Any request started before an invalidation is fenced from
  joining and from landing in the cache — including superseded
  requests the inflight map no longer tracks.

Also: delete_workspace_item invalidates comparisons like deploy does
(deployed state moved), and empty FILTERED indexes say the filter
matched nothing instead of claiming workspace/fork parity.

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

* fix(ai): invalidate comparisons on every direct deploy; keep secret caveat with metadata changes

Direct chat deploys (schedule/trigger/resource/variable/app) bypass
deployDraftToWorkspace and never evicted cached fork comparisons — the
shared deploy tail now invalidates before the fallible draft cleanup.

A secret variable whose metadata also changed produced a non-empty patch
that silently dropped the value-uncomparable caveat; item reads, the
index, and fork sections now keep the caveat alongside the patch.

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

* fix(ai): scope diff caches to the authenticated identity; derive fork freshness from the comparison store

An SPA logout/login left workspace-keyed diff caches (per-user drafts,
permission-filtered fork patches) readable by the next account — both
cache modules now wipe on identity change, with a global generation
floor fencing requests started under the previous account.

The fork snapshot stamped its own fetchedAt over a comparison that
could already be near expiry, compounding the two 30s windows, and
survived comparison-store invalidation when draft cleanup failed after
a deploy. It now carries the comparison's own fetchedAt/generation and
stops reuse the moment the store fences it. Delete-item invalidation
moved before the fallible draft cleanup for the same reason.

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

* fix(ai): fence fork-reconciliation joins across account switches

ForkCache lacked the epoch stamp WorkspaceCache carries, so a joiner
arriving after an identity change (or any epoch bump landing before it)
compared its own post-bump epoch against itself and adopted the old
producer's in-flight tally. The cache now records its producer's epoch
for the joiner and reuse gates, and an identity change also discards
the in-flight reconciliation maps so no cross-identity join exists.

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

* fix(ai): surface swallowed fork-side fetch failures; include conflicted editor edits in item diffs

The shared getItemValue reads {} for any failed fetch, so a transient
API failure on a fork side rendered as a fabricated one-sided diff (or
parity when both sides failed). A fork side is only fetched when the
comparison lists it as existing, so an empty read now raises and shows
as a fetch-error entry.

Item reads promised conflicted local edits (the index says so) but the
local-override branch only covered autosave-off and failed saves — a
conflict silently fell back to the persisted draft. Conflicts now read
the in-memory editor value too, with a caveat naming which side is
shown either way.

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

* fix(ai): report failed diff materializations as unsearched instead of silently omitting them

A side-fetch failure left an index entry with status 'error' and no
patch; diff search skipped it and still presented definitive no-match
or complete-count results. Failed entries are now listed in a warning
naming what was not searched.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 17:06:32 +00:00
hugocasa 0e04bc6991 fix: steer ai chat away from draft-blind api catalog reads and runs (#10202)
* fix: steer ai chat away from draft-blind api catalog reads and runs

* fix: support api catalog tools in ai eval harness mock backend

* fix: block remaining draft-blind read and list endpoints in api catalog

* fix: scope eval fetch stub to handled benchmark api paths

* feat: add deployed version read option to read_workspace_item

* fix: include input schema in script workspace item reads

* feat: add pagination to list_workspace_items

* fix: paginate list_workspace_items per item type without cross-type loss

* fix: window draft overlay in list_workspace_items by page and limit

* refactor: simplify list draft overlay to capped page-1 merge

* fix: label server-synthesized draft-only rows as drafts in listings

* chore: fix stale eval comment and pin draft_only listing label
2026-07-20 18:45:14 +02:00
0ea570570e feat(ai-sessions): CRUD markdown artifacts in sessions (#10046)
* feat: add IndexedDB persistence layer for AI-chat artifacts

* feat: add reactive store for AI-chat artifacts

* feat: add artifact chat tools and wire store lifecycle

* feat: add markdown artifact viewer with source toggle

* feat: surface session artifacts in the preview panel and chat list

* feat: tell the copilot when to use artifacts in the session prompt

* test(ai_evals): add artifact case and wire artifact helpers for session context

* fix(copilot): keep in-memory artifacts across same-session resyncs

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

* feat: unify session composer edits/artifacts/jobs into a status line

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

* feat: add an artifacts section to the session preview picker

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

* feat: share markdown prose presets and restyle the artifact viewer

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

* refactor: unify session status popovers into one keyboard-navigable shell

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

* fix: reset first-block top margin in all markdown prose presets

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

* fix: open the preview picker on the artifacts branch for an active artifact

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

* fix: keep artifact picker scope independent of branch hydration state

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Guilhem Lemouel <guilhemlemouel@gmail.com>
2026-07-16 11:17:20 +02:00
056ebdb035 fix: read chat drafts via own-draft route so drawer-kind drafts deploy (#9913)
* fix: read chat drafts via own-draft route so drawer-kind drafts deploy

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

* test: cover trigger and resource chat-draft read/deploy regressions

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

* test: cover non-secret variable chat-draft read/deploy regression

Completes the drawer-kind matrix from the review notes on #9913: schedule,
trigger, and resource already had full write→read→deploy regressions; this
adds the variable one (non-secret — the secret flow deploys through the
ephemeral in-memory value and is pinned by the existing ephemeral tests).

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

* fix(ai_evals): mock getOwnDraft so eval draft hydration stays in-memory

The frontend eval adapter intercepts DraftService for benchmark workspaces,
but only updateDraft/getDraftForUser/listDrafts. Global eval output
collection hydrates draft values through getGlobalDraft, which reads via
getOwnDraft — so draft-producing global cases fell through to the real
generated client instead of the in-memory benchmark store. Adds a
getBenchmarkOwnDraft helper (null on miss, mirroring the 200/null route
semantics), wires it into the adapter mock, and pins it in
mockBackendDrafts.test.ts.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-07-06 11:34:08 +02:00
centdixandClaude Opus 4.8 44c25de418 feat(ai-chat): add create_folder tool to global chat (#9819)
Global-mode chat could reference the user's existing folders in the system
prompt but had no way to create a new one, so for shared work where no
existing folder fit it would dead-end on "ask the user" or invent a
non-existent f/<folder>/… path (which fails at deploy).

- create_folder: dedicated, confirmation-gated tool for the immediate
  (non-draft) folder mutation; the creator becomes an owner. Mirrors the
  backend name validation client-side and returns a minimal { success } result.
- Folder path guidance now steers the model to create a folder only when the
  user explicitly asks for one, and otherwise to ask which folder to use for
  shared intent rather than guessing or inventing a path.
- ai_evals: in-memory create_folder mock + a create-folder case (global-path5);
  path3 maxTurns bumped to give room to ask.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 18:57:31 +02:00
centdixandClaude Opus 4.8 b0ddcf31e4 ci: add path-gated AI agent + ai_evals smoke workflows (#9640)
* ci: add path-gated AI agent integration tests workflow

Runs integration_tests/ai_agent_tests against real LLM providers
(Anthropic/OpenAI/Google) only when AI-agent backend code or the tests
change, since runs make paid LLM calls. Adds a conftest fixture that
skips provider-parametrized cases whose API keys are absent, so CI
exercises only the providers it has secrets for.

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

* ci: add path-gated ai_evals global-mode smoke workflow

Runs the global AI chat eval (global-test1) across one cheap model per
provider (Anthropic/OpenAI/Google/DeepSeek) only when the eval harness or
copilot chat code change, since runs make paid LLM calls. Builds Windmill
CE from source as the AI proxy; global tools/drafts run in the Vitest
bridge. Gates on the deterministic draft pipeline (run succeeded +
produced a draft + used write_script), not the variable LLM judge score.

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

* ci: run AI smokes on PR ready-for-review instead of every push

Switch the pull_request trigger from `synchronize` (every commit) to
`ready_for_review`, with a job guard skipping draft PRs, so the paid LLM
runs only fire when a PR is marked ready to merge (plus push-to-main and
manual dispatch).

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

* fix(ai_evals): lazily load cli mode so non-cli evals skip the cli toolchain

The entrypoint eagerly imported modes/cli, which pulls the wmill CLI
guidance modules and their JSR deps (@cliffy/*). Global/flow/script/app
runs then crashed with "Cannot find module '@cliffy/ansi/colors'" when
the cli workspace deps were not installed. Import createCliModeRunner
dynamically inside runCliBenchmark instead.

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

* test(ai_agent): raise low max_completion_tokens to OpenAI's 16 minimum

OpenAI's /v1/responses rejects max_output_tokens < 16 with a 400, failing
test_low_max_tokens for openai. 16 still exercises a truncated response.

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

* ci: run ai_evals workflow on Node 22 for the frontend undici 8.x dep

The Vitest bridge loads frontend/node_modules/undici@8.x, which requires
Node >=22.19; Node 20 failed with "webidl.util.markAsUncloneable is not a
function" when loading vitest.config.ts.

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

* fix(ai_evals): run frontend evals autonomously + give global-test1 more turns

Frontend evals (flow/script/app/global) ran the production chat prompt, which
assumes an interactive human — so cheaper models burned their turn budget
asking for confirmation, waiting for approval, or presenting a plan, sometimes
hitting maxTurns without producing a draft. Append a shared autonomy note in
baseEvalRunner (the path all frontend modes share, mirroring cli mode): act
directly on clear requests; only ask on genuinely ambiguous ones (preserving
the askUserQuestion cases). Also raise global-test1's maxTurns 8 -> 10 so a
model that over-explores still converges.

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

* ci(ai_evals): watch draft/prompt deps outside copilot/

The global eval runs production frontend code in-process, so the smoke's
behavior depends on files outside frontend/src/lib/components/copilot/**:
the draft model (userDraft.svelte.ts, userDraftDbSyncer.svelte.ts), script
inference (infer.ts), and the chat system prompts ($system_prompts ->
system_prompts/auto-generated). Add them to both push and PR path filters so
a change there actually triggers the smoke that gates on draft production.

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

* fix: skip direct provider tests without credentials

* feat: add ai evals skip judge flag

* fix: simplify ai evals ci gate

* fix: simplify ai evals smoke gate

* fix: handle ai eval workflow triggers

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:41:31 +02:00
centdixandClaude Opus 4.8 74a2329d2e feat(copilot): improve global-mode path selection + add path-selection evals (#9698)
* test: add global-mode path-selection eval cases with seeded user

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

* feat(copilot): guide global-mode path selection with injected folder list

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

* fix(copilot): tailor global-mode folder guidance for workspace admins

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

* fix(copilot): type folders_read; isolate global-eval user from store

Addresses PR review:
- Add folders_read to the User/whoami openapi schema and UserExt; the global prompt builder and eval harness now read it typed instead of via inline casts (regen the client to pick it up).
- prepareGlobalSystemMessage takes an explicit user; the eval harness passes it rather than mutating the process-global userStore, removing the concurrency race (path cases no longer need --verbose).
- Rewrite the path-selection case comment as a current invariant.
- Add buildFolderGuidance unit tests in core.test.ts.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 11:59:59 +02:00
centdixandClaude Opus 4.8 4296a6ae1f feat(ai-chat): cap read_app_file + search_app grep tool to bound context in large raw apps (#9653)
* docs: add global AI chat context-optimization plan for raw apps

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

* test(ai-evals): add global raw-app debugging cases on a large fixture

Adds a ~20-file analytics_dashboard raw-app fixture (incl. a 5k-line data module
and a planted wrong-totals bug), two global cases (read-heavy debug + small-edit
baseline), app-seed support in the mock backend, directory-fixture loading, and a
decorateHelpers seam so read-dedupe is measurable. Records tokenUsage for before/
after comparison of the read-tool optimization.

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

* feat(ai-chat): cap and dedupe read_app_file to bound context in large apps

read_app_file now defaults to a head slice (1500 lines / 50k chars) with offset/
limit to page further, and skips resending a file whose earlier read is still in
context (per-conversation ledger keyed off the originating tool-call id, so it
self-heals after compaction). Bounds the file-content portion of global-chat
context when working in large raw apps.

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

* test(ai-evals): add read-heavy raw-app debug case (large data module)

global-test31 induces the model to inspect the 5k-line seedData module, exercising
the read_app_file cap/offset path. Baseline ~262k tokens vs ~200k with the cap+dedupe
change (-24%).

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

* docs: record A+B benchmark results and fixed-overhead finding

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

* fix(ai-chat): clearer read_app_file past-EOF message + unit tests for cap/dedupe

Addresses local-review nits: out-of-range offset now reports 'offset N is past the
end of the file' instead of a backwards 'lines 11-10' label; adds unit coverage for
the slicing (line cap, offset/limit window, char budget, past-EOF) and re-read dedupe
(hit + miss-when-not-retained).

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

* feat(ai-chat): char-level paging + per-range dedupe for read_app_file

Adds char_offset/char_limit so minified/long-line files can be paged within a line
window, keys the re-read ledger by range (so reading different ranges no longer
collides), and dedupes on the full-file hash (a cached range stub is invalidated
when any byte of the file changes, not just the returned range). Tests updated for
the char-slice behavior plus single-line capping, char paging, and out-of-window
change detection.

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

* test(ai-chat): add read_app_file context micro-benchmark + re-read eval case

Adds a deterministic micro-benchmark (no LLM) that drives read_app_file through a
realistic big-project read pattern (large file, re-read, minified bundle, paging)
and asserts the cap+dedupe cut returned context >50% vs the old whole-file behavior
— isolating the feature's effect from model nondeterminism and guarding against
silent weakening. Adds global-test32, a cross-file consistency investigation that
revisits overlapping files so re-read dedupe is exercised in a real run.

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

* test(ai-evals): clarify test32 measures the read cap, not dedupe

Verified: sonnet and haiku both read each file once per conversation and retain
it, so test32 never triggers read_app_file re-read dedupe. Dedupe is measured
deterministically by the micro-benchmark instead. Comment corrected to match.

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

* refactor(ai-chat): drop read_app_file re-read dedupe, ship the cap only

Benchmarking showed the per-conversation re-read dedupe never fires in practice:
across sonnet/opus/gpt-5.5/haiku, every model reads each file once per conversation
and keeps it in context (0 within-conversation re-reads). It was a correct but unused
guard, so this removes the ledger, full-file hash, retention predicate, the
AIChatManager wiring, and the eval decorateHelpers seam — keeping the read cap +
offset/limit/char paging (A), which is the lever that actually bounds context. The
micro-benchmark is now cap-only; test32 is kept as a multi-file read-load case.

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

* feat(ai-chat): add search_app grep tool for global raw-app chat (experimental)

Client-side grep over a raw app's frontend files and inline runnables (literal,
case-insensitive, optional file_glob/context_lines/max_matches, head-capped).
Completes the list -> search -> ranged-read triad. Includes the eval A/B gate
(WMILL_AI_EVAL_DISABLE_SEARCH_APP), unit tests + micro-benchmark, and a
find-all-usages eval case (global-test33).

Experimental: A/B benchmarking shows it is not an unconditional win — it helps
on find-all-usages but adds agentic iterations on navigable apps.

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

* test(ai-evals): accept search_app as a valid file-inspection tool in raw-app cases

Add requiredToolsAnyOf alternatives-group to ToolValidationSpec and switch
global-test29..32 to it so a model that locates files via search_app instead
of read_app_file no longer false-fails the tool assertion.

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

* docs: remove stale ai-chat context-optimization planning doc

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

* refactor(ai-chat): drop read_app_file char paging for a hard char cap

The char_offset/char_limit params guarded minified files (a single line over
the char budget) but were effectively unused in benchmarks. Remove them and the
in-window char paging; keep the hard 50k-char budget and, when a read hits it,
tell the model to narrow the line limit (or treat the file as unreadable if a
single line exceeds the budget). Proper long-line handling is left as a TODO.

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

* refactor(ai-chat): bake search_app context to 1 line, clarify query is literal

Drop the context_lines param (models varied it to little effect) for a fixed
SEARCH_APP_CONTEXT_LINES=1, and cap on matching lines instead of pushed rows so
max_matches stays accurate with context always on. Sharpen the query description
to state it is a literal (non-regex) substring and to suggest the call form
(e.g. formatCurrency() to hit call sites and skip formatCurrencyPrecise.

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

* refactor(ai-chat): widen baked search_app context to 2 lines

Models that set the old context_lines param leaned to 2; match the lean.

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

* fix(ai-chat): count every file with a match in search_app header

Move fileHadMatch ahead of the render cap so files whose matches fall past max_matches are still counted (with a regression test). Also swap the raw NUL globstar sentinel for a printable escape (the NUL bytes made core.ts read as binary to grep) and reword two comments to describe current constraints instead of drafting history.

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

* fix(ai-chat): drop redundant input echoes from app tool results

read_app_file and search_app no longer prefix results with the tool name or echo back the caller's own inputs (file path, query, file_glob) — the model already has them from the call args, and the unbounded query echo could push the search result past its output budget. Keeps the useful signals (line range, match/file counts, truncation) and the actionable advice. Also reword max_matches to 'matching lines' since it caps lines (each expands to context rows). Unit tests updated to the new format.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 13:33:54 +00:00
centdixandClaude Opus 4.8 3f5f211a22 add final context size metric to ai_evals harness (#9660)
Record finalContextTokens per attempt: the input-token total of the last
model request (input + cache-creation + cache-read), i.e. how full the
context window ended up. Complements the cumulative tokenUsage.prompt,
which conflates context size with loop-iteration count.

Captured generically in the shared frontend runEval via the chat loop's
lastIterationUsage, so it covers all frontend modes (global/flow/script/
app), plus CLI mode via the last assistant turn's usage. Aggregated as
average and max over passed attempts and printed in the run summary.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 23:21:28 +02:00
centdixandClaude Opus 4.8 e87ff79ecf fix(ai_evals): adapt global eval harness to DB-backed user drafts (#9641)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 16:53:21 +02:00
centdixandClaude Fable 5 f4425fca9f feat(ai-chat): self-hosted docs tools via windmill.dev llms.txt + ask benchmark (#9578)
* feat(ai-chat): add self-hosted docs tools fetching from windmill.dev llms.txt

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

* test(ai-evals): add ask benchmark mode comparing inkeep vs llms.txt docs tools

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

* test(ai-chat): fix docs link sanitizer tests to match skip-all-`../` guard

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

* feat(ai-chat): add hybrid full-text docs search tool and ask variant

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

* feat(ai-chat): expose docs search tools in the global workspace assistant

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

* refactor(ai-chat): drop inkeep/llmstxt arms, keep only hybrid docs search

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

* docs(ai-chat): remove docs-tool benchmark write-up

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

* refactor(ai-evals): remove ask mode, cover docs search via global mode

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

* nits

* refactor(ai-chat): swap navigator + api copilots from inkeep to search_docs

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

* fix(ai-chat): point read_docs_page empty-path hint at search_docs

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-17 15:01:03 +02:00
centdixandClaude Opus 4.8 cfe5119035 feat(ai): add list_runs and get_job_logs tools to global chat mode (#9488)
* feat(ai): add list_runs and get_job_logs tools to global chat mode

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

* refactor(ai): always suppress ansi hint in get_job_logs, drop misnamed param

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

* test(ai_evals): add global list_runs and get_job_logs eval cases

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

* style(ai): trim get_job_logs description and format global core

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

* fix(ai): surface list_runs/get_job_logs output as tool result

The tools set showDetails but never set message.result, so the details panel rendered "No result yet" even on success. Set result in setToolStatus (logs go in result for get_job_logs).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 16:34:36 +00:00
centdixandClaude Opus 4.8 343368fb5e test: add datatable tool coverage to global AI evals (#9398)
* test: add datatable tool coverage to global ai_evals (stage 0+1)

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

* test: add seeded datatable difficulty-ladder global ai_evals (stage 2)

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

* test: skipJudge datatable evals and make stringIncludesAnyOf existential

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

* test: make ai_evals datatable mock reflect SQL writes

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:34:37 +00:00
centdix f300a716a9 test: add global chat resource variable schedule evals (#9379) 2026-05-30 10:02:36 +00:00
centdix 9e7eaf3684 feat: inject active editor into global chat (#9361) 2026-05-28 13:57:49 +00:00
centdix e29dfbaa87 test: add global chat eval coverage (#9320)
* test: improve global chat eval parity

* test: add human-style global chat evals
2026-05-27 12:19:57 +00:00
centdix 4be930f585 refactor: unify AI provider credentials (#9317)
* refactor: use provider credentials for worker builders

* refactor: resolve api proxy credentials directly

* fix: lazy load frontend eval modes
2026-05-26 05:51:32 +00:00
centdix fec4008696 fix: preserve ai reasoning content (#9208)
* fix: preserve ai reasoning content

* fix: avoid text-only reasoning replay

* feat: add deepseek ai eval models
2026-05-18 10:40:18 +00:00
centdix 7a7d246a6e test: add global ai eval mode (#9129)
* feat: add global ai eval mode

* fix: improve global eval validation feedback
2026-05-13 08:20:30 +00:00
centdix a305a74e73 refactor: require ai evals proxy backend (#9119) 2026-05-12 13:44:00 +00:00
centdixandClaude Opus 4.5 b883f9a9d2 feat: add ai chat schedule and trigger tools (#8961)
* feat: add ai chat schedule and trigger tools

* refactor: use zod for ai chat workspace tools

* refactor: let ai provide runnable target fields

* refactor: generate ai chat workspace tool schemas

* fix: add object type to composed tool schemas

* fix: avoid top-level trigger schema unions

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

* fix: block undeployed workspace ai tools

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

* fix: inject ai workspace tool target

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

* test: add ai evals for workspace tools

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

* test: make workspace tool eval prompts realistic

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

* fix: surface workspace tool errors

* fix: show workspace tool success details

* fix: describe workspace tool path format

* fix: clarify workspace path examples

* fix: tighten workspace tool validation

* fix: align workspace tool prompts

* chore: mark generated chat schemas

* chore: mark generated cli skills

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-29 14:00:01 +00:00
centdixandClaude Opus 4.5 483fb1fb9a perf: reduce app ai chat token usage (#8928)
* test: add app chat token usage evals

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

* perf: make app file listing metadata only

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

* perf: reduce app datatable prompt context

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

* test: add app datatable persistence eval

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

* test: fix file manager rename app eval

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

* test: remove selected app context eval cases

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

* fix: address app eval review feedback

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-24 19:49:16 +00:00
centdixandClaude Opus 4.5 434113b5fd tests: add cli eval behavior checks (#8899)
* feat: add cli eval behavior checks

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

* fix: harden cli eval command parsing

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-21 16:17:40 +00:00
centdixandClaude Opus 4.5 fddd8e288f fix: add proxy eval coverage for gemini schemas (#8897)
* feat: add proxy transport for ai evals

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

* fix: strip propertyNames for gemini schemas

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

* fix: require explicit eval transport

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-21 16:03:44 +00:00
centdixandClaude Opus 4.5 a5363ea4ed refactor: unify flow chat tree operations (#8862)
* refactor: make flow chat code edits explicit

* refactor: centralize flow tree lookups

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

* refactor: simplify flow chat tree mutations

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

* refactor: reuse flow tree lookup in schema map

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

* refactor: remove flow lookup alias

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

* refactor: reuse flow tree in previous results

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

* refactor: reuse canonical flow module lookup

* fix: align rebased flow helpers

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

* docs: remove flow chat cleanup plan

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

* refactor: remove flow chat helper wrappers

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

* fix: preserve non-flowmodule AI agent tools in skeleton and previous_result

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

* refactor: consolidate flow module ID collectors into flowTree

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

* fix: search full flow tree in test_run_step to find special modules

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

* fix: recurse into aiagent tools in collectAllFlowModuleIds

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-20 17:50:50 +00:00
centdixandClaude Opus 4.5 46b2915a9d feat: improve app evals and localized app edits (#8863)
* chore: record app benchmark baseline

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

* feat: strengthen app benchmark persistence checks

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

* feat: seed inventory tracker benchmark case

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

* feat: add deterministic app diagnostics

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

* feat: add app chat patch_file tool

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

* test: add app session id micro-edit case

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

* fix: narrow app patch file content

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

* fix: stop gating app evals on lint

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-20 11:56:35 +00:00
centdixandClaude Opus 4.5 51b09ace45 feat: add empty inline script warnings to flow chat (#8853)
* fix: seed empty inline flow scripts

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

* fix: cap frontend eval chat turns

* fix: roll back failed inline script seeding

* refactor: simplify inline flow script warnings

* refactor: share flow module traversal

* refactor: make flow chat code edits explicit

* fix: resolve ai tool review actions

* refactor: remove dead flow rawscript helper

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-17 12:10:26 +00:00
centdixandClaude Opus 4.5 b39671d933 feat: add compact json patch tool to flow chat (#8840)
* fix: use compact json for flow patches

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

* test: improve flow eval harness

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

* test: record flow benchmark history

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

* fix: preserve schema in set flow json

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

* style: clean set flow json schema guard

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

* fix: clean flow patch review followups

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-16 14:31:05 +00:00
centdixandClaude Opus 4.5 d3cb0c6220 fix: improve flow chat and benchmark coverage (#8825)
* fix: support special flow modules in evals

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

* refactor: extract shared flow helper logic

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

* fix: make special flow tools openai-compatible

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

* fix: improve flow eval prompts and validation

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

* test: relax flow benchmark overfits

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

* test: record updated flow benchmark history

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

* fix: address flow review findings

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

* refactor: source flow chat special module prompt

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

* fix: narrow rawscript helper return type

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

* refactor: dedupe flow chat prompt guidance

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

* fix: relax flow test10 validation

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-15 16:22:39 +00:00
centdixandClaude Opus 4.5 f1e84cb088 chore: add backend preview validation to ai evals (#8827)
* feat: add backend preview validation to ai evals

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

* fix: refresh shared preview workspace assets

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

* fix: harden shared backend preview validation

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-15 15:11:25 +00:00
centdix cdcc56461b feat: add black-box ai eval benchmarks (#8618) 2026-04-13 14:05:46 +02:00