mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-04 08:02:23 +00:00
e1e3692fbc82d50c021ef8bf8ca7019d760705f0
42
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6e1ef93f32 |
feat: let test_run_flow name the conversation of a chat-mode test run (#11198)
* feat: let test_run_flow name the conversation of a chat-mode test run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: refuse a non-UUID conversation_id and mint chat test-run ids in one place Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(ai-evals): add a chat-flow follow-up case and mock flow preview runs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: expect the conversation id argument on the manager's flow test bridge Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(ai-evals): require the chat-flow follow-up runs to share one conversation id Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: name the test_run_flow argument memory_id after the run parameter Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d8b9174235 |
feat(ai-sessions): turn skills on by default, and group them by folder (#11058)
* feat(ai-sessions): turn skills on by default, and group them by folder A skill is instructions the workspace wrote for the assistant to use, so what carrying one costs is context rather than access. Selecting each one before it applied made publishing a skill a two-step affair, and left most of them unused. Skills now default to on. No storage is rewritten to get there: the preference keeps its key and holds a decision per path, so the older array of enabled paths still reads as "these were on" and only the paths nobody decided about move. MCP servers stay opt-in through the same factory — their tools reach an external system, which is a different question from context. The Skills settings list groups into a tree once skills span more than one folder, with a switch per folder acting on everything beneath it, and the list answers the keyboard: Up/Down walk it, Left/Right fold, Space flips the switch under the highlight, Enter opens the skill. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: give a modal the option to stand only as tall as the window `AIPromptsModal` asks for 1000px of height, which is taller than a laptop window: the dialog then scrolled inside the overlay while its list scrolled inside the dialog — two scrollbars, one of them moving the modal itself. The cap `Modal2` appeared to have, `max-h-screen-80`, is defined nowhere in the tailwind config, so it never applied to anything. `fixedHeight="viewport"` is a new value that stands as tall as the window allows. Deliberately a definite height rather than a max-height: bodies here size against the box with `h-full` / `grow min-h-0` and scroll inside it, and a max-height leaves them nothing to resolve against — they grow past the surface instead. Every existing size keeps the height it has today, so no other modal moves. The two classes that resolved to nothing are removed. The prompts modal and the assistant settings modal take the new value; both already scroll inside themselves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: pin read_skill's gate in its test, and say who a delete affects The refusal `read_skill` gives for a path that is not a skill changed shape — it checks the workspace listing now, not just the off-switch — and its test was still asserting the old wording against an unmocked listing. The delete confirmation said everyone "who selected it" loses the skill, which stopped being true when skills started defaulting to on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: keep the keyboard walk when the list scrolls under the pointer The mouse takes the skills list back on a real movement over it, not on `mouseenter`. The browser fires that one whenever rows arrive under a stationary pointer — every scroll the keyboard itself causes, and every folder collapse — so walking Down past the bottom of the list handed control back to a mouse nobody had touched, and the next press restarted at the top. Also from the review round: the "+" menu sorted skills on-first, a key that is constant now that they start on, and pushed the one row it did move — a skill just turned off there — out of the shortcut that turns it back on. It orders by path. The remaining "selection" wording follows the vocabulary the rest of this change moved to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: carry the keyboard walk on from the row the mouse left it on Handing the list to the mouse dropped the highlight, so the next arrow press started again at the top. It moves to the row under the pointer instead — invisible while the mouse leads, since drawing and acting both wait on the keyboard being in charge, and exactly where someone would expect the walk to carry on from. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: cap the AI prompts modal from its own call site Reverts `Modal2` and the assistant settings modal to what they were. The prompts modal asks for `xxl`, 1000px, which is taller than a laptop window, so the dialog scrolled inside the overlay while its list scrolled inside the dialog. It now passes `max-h-[80vh]` through the `css.popup` the component already forwards. The height stays definite underneath, which is what lets the list bound its own scroller, and nothing outside this one modal changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * refactor: drive the skills list highlight with useListHighlight The Tools section next door already had this: `useListHighlight` owns the highlighted index, wrapping, `scrollIntoView`, and the rule that a scroll under a resting pointer must not hand the list back to the mouse — the bug this section rediscovered the hard way. Reusing it drops the parallel implementation. What stays local is what is actually a tree: Left and Right fold a folder or step into it, Space flips the switch under the highlight, and Enter opens the lit skill. `restingIndex` is what keeps the highlight on a folder through a fold, where a search would instead send it back to its top hit. The keys are answered at the window rather than on the list: leaving the editor parks focus elsewhere, and a container-scoped handler goes silent when it does. `move` is now returned by the composable, for the step into a folder's children. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: stop the fold's sticky row resetting the keyboard walk `stickyKey` is read through `restingIndex`, which `useListHighlight` calls inside the effect that reacts to the row count. As `$state` it was also a dependency of that effect, so clearing it on the next arrow re-ran the effect and wrote the highlight back to nothing: after collapsing a folder, one Down lit nothing and the one after it started again at the top. It is a plain variable now, read when the effect runs and invalidating nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: keep one lit row, and keep the fold's sticky row to its fold Three from the review of the `useListHighlight` swap: The sticky row a fold takes is now given up as soon as that fold has rendered. Held until the next arrow, it pulled the highlight back to that folder on any later change — another fold, a save, a delete, a workspace switch. Space and Enter on a focused control bring the highlight to that control's row before the control answers them. A switch keeps focus after a plain click, and the row drawn as highlighted was then a different one from the row that flipped. Up and Down carry on from a row reached with Tab. `useListHighlight` cannot see that by itself: `ListRow` puts the row's id on its outer div while focus sits on the button inside it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: land the highlight on a named row rather than stepping to it `move` counts steps from wherever the highlight is, and from nothing lit it can only reach an end of the list — so the three places that meant "put it on this row" (a row reached with Tab, the row of a focused control, a folder's parent) sent it to the first row whenever nothing was lit yet. `useListHighlight` grows a `moveTo` for naming the row outright, and those three use it. The handler's own doc still said the keys are answered on the list; they went back to the window when the editor's page transition proved able to take focus away from it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija * fix: fold from the header click the way every other fold does The header's own click wrote `collapsed` directly instead of going through `fold`, so the row count changed with no row named to keep: the highlight reset, and since the highlight is the header's only hover feedback, it went flat under a pointer that had not moved and stayed flat. Also from the round: a duplicated `svelte-ignore`, the missing one on the header wrapper that takes `onmouseenter`, and a trailing comma prettier wanted gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JWUQ867ZJCZJmkWUxqHija --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
fa73539839 |
fix(ai-chat): test_run_flow could test a different flow than the one asked (#11066)
* fix(ai-chat): test_run_flow could test a different flow than the one asked Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmQDoFXVPwYyEN8PfV2oL7 * fix(ai-chat): prefer the flow editor stored at the path over one renamed to it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmQDoFXVPwYyEN8PfV2oL7 * test(ai-chat): default the flow helpers factory and trim duplicated setup Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmQDoFXVPwYyEN8PfV2oL7 * refactor(ai-chat): resolve the flow editor to run by its storage path Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmQDoFXVPwYyEN8PfV2oL7 * refactor(ai-chat): move the editor storage path context out of sessions Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmQDoFXVPwYyEN8PfV2oL7 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a6abf2c8a7 |
feat: run and test scripts from the AI chat through an argument form (#11001)
* fix: disable a dynamic input when its schema field is disabled Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * refactor: extract the run form's argument hygiene into job_args Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: give Tabs an opt-in sliding selection indicator Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * refactor: share the chat's scroll-fade measurement Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: give the chat a run-form contract and incremental job output Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: run and test a script from the chat through an argument form Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: carry a chat run's card and job across saves and reloads Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: render a chat run as a tool call row with its form, logs and result Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: open a pending run form in the sessions preview pane Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * test: benchmark running a deployed script from the chat Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERed9zo2oJjpSczzMayaNh * feat: offer a test run's dynamic options from the draft it previews Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: say that a test run's dynselect helper executes on form display * fix: send a schema default the model omitted when yolo skips the form * fix: infer a test run's schema when the stored one declares no properties * docs: tighten the note on the form's mount-time helper job * fix: apply a nested schema default the bypass posture counts as answered * fix: apply a declared default to a null value and an optional nested field * fix: check required fields inside a supplied optional object before bypassing * fix: read required args as own properties before bypassing the form * fix: stop the turn from the run form's action row in the preview panel * refactor: drop the run-form prediction and share its secret minting * refactor: prefill a proposed secret instead of emptying the field * docs: correct the comments the run-form prediction left behind * fix: keep a proposed secret out of the chat's stored messages * docs: say what a literal secret argument now does * test: restore the copilotInfo export the aiStore mock omits * docs: cut the run form's helper-script note to its constraints * refactor: settle a run form from one entry and fetch a job's logs once * fix: separate colliding secret paths, gate plan mode, keep polled logs * fix: mint before the form opens, skip empty fields, show what ran * revert: mint a run form's secrets at submit, not before it opens * fix: settle a cancelled run card on the form's arguments, not the proposal * fix: settle a stopped run form like a cancelled one, and keep an empty secret empty * fix: snapshot a run's arguments before minting its secrets --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
816dc9dcd2 |
feat(ai-sessions): show a running session across tabs and reload finished turns (#10916)
* fix(ai-chat): make a disabled composer look disabled Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(ai-sessions): show a running session across tabs and reload finished turns Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): keep queued drafts through catch-up and hold locks by identity Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): carry pastes through refusals, spare resends and auto-resume Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): retry held auto-resume, keep the footer, spare bfcache freezes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): give each driving tab its own lock slot Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): release refused synthetic sends and use a text key separator Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): merge late-refusal restores and keep attachment-only edits Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): patch the stored chat pointer instead of rewriting the record Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * docs(ai-sessions): align the run-signal comments with the code Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * fix(ai-sessions): retry transient catch-up skips and gate the remaining send paths Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt * docs(ai-sessions): name the chat-id seeding path persistTouched defers to Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDUsDEbycDCBTAH2x8jUAt --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
cfcfe298dd |
feat(ai-chat): make reusable skills ai_skill resources you select per workspace (#10914)
* feat(ai-chat): make reusable skills ai_skill resources you select per workspace * chore: pin the ee ref to the skill telemetry counters * fix: address review findings on skill authoring, import and migration * fix: enforce skill selection in read_skill and stop imports clobbering resources * feat: carry format_extension from the hub into synced resource types * fix: let an edit set or clear a resource type's format_extension * fix: regenerate the sqlx cache and close the review round findings * fix: close the round-2 findings on folder ACLs, cached sync and truncation * refactor: make the skills migration non-destructive and use design-system inputs * fix: close the round-4 findings on folder owners, startup sync and truncation * fix: clear obsolete extensions, guard folder owners, and report skipped skills * fix: honor explicit-null extensions and report same-type migration conflicts * fix: scope skill actions to the committed workspace and paginate the listing * fix: keep the drawer scoped to the live workspace and surface truncation * fix: discard a skills refresh for a workspace the chat has left * chore: update ee-repo-ref to 6efe7a73c745c2e1377a34498523c00d89010a3d This commit updates the EE repository reference after PR #764 was merged in windmill-ee-private. Previous ee-repo-ref: 55998c142bc72edd08532748af1974b16035658d New ee-repo-ref: 6efe7a73c745c2e1377a34498523c00d89010a3d Automated by sync-ee-ref workflow. --------- Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
c512110a1f |
fix(ai-chat): consume an @ mention with the message that carried it (#10907)
An `@`-mentioned workspace item stayed in `selectedContext` after the message that mentioned it was sent, so every later turn in the session restamped it into `## SELECTED CONTEXT`. Treat those mentions the way a DOM pick is treated: attached to the one message that carried them. The composer pins the live selection as `contextOverride` at the click and clears the mentions in the same synchronous gesture, so the send keeps what the user picked for it and the next draft starts clean. When a send hands its text back to the composer, the mentions it carried come back with it. Scoped to GLOBAL. In SCRIPT/FLOW/APP the mentions still stay selected as chips the user removes by hand, so `isMentionContext` is membership only and every caller gates on mode. Claude-Session: https://claude.ai/code/session_01HxGz1YsvW5Kwmn8THAUrwB Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
25a3e6ea7a |
fix(ai-chat): keep the composer usable while a question is pending (#10816)
* fix(ai-chat): keep the composer usable while a question is pending Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL * fix(ai-chat): keep a typed answer when the question's resolver is gone Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL * fix(ai-chat): only advertise the answer affordance on a live question Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL * style: trim the pending-question rationale comments to the 4-line cap Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2hyAfRT2aFF7uswsdTodL --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
541b6c8496 |
fix: keep ai chat messages when leaving the page mid-generation (#10809)
* fix: persist ai chat turns mid-generation so leaving the page keeps them * fix: stop chat checkpoints once the turn commits, keep streamed text visible * fix: checkpoint streamed answers as they grow and keep half-run tool batches * fix: checkpoint text as received so a backgrounded tab keeps capturing * fix: keep buffered tool screenshots in mid-batch chat checkpoints * fix: decide committed-text at the flush site, condense checkpoint comments * fix: checkpoint only live streamed text, never text the parser owns * fix: don't swap the chat transcript out from under a running turn * fix: close the pre-loading window in the conversation-switch guard |
||
|
|
0b3dc3e5c9 |
fix: build the global chat's prompt identity from the operating workspace (#10793)
* fix: do not read an unloaded workspace list as a non-membership `roleForWorkspace` settled `not_a_member` from `userWorkspaces` alone. That store and `superadmin` both start undefined and load asynchronously, so an unloaded list read as an empty one: a chat operating on any workspace other than the one being browsed advertised no pages and reported an access denial. The root layout gives up after its retries, so a load that fails leaves the denial permanent, with `whoami` never attempted. Settle a non-membership only once both stores have resolved; treat unresolved as unknown and fall through to the `whoami` lookup. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: build the global chat's prompt identity from the operating workspace The prompt's path conventions and folder guidance came from the ambient `userStore`, which describes the workspace being browsed rather than the one the chat operates on. Three of those fields are per-workspace and wrong whenever the two differ: the username (that workspace's `usr` row), the writable/readable folder sets (its ACLs), and `is_admin`, which decides whether the folder list reads as exhaustive. The backend still enforces the ACLs, so the cost is prompt quality — paths the model cannot write to, and a 403 to recover from. Resolve the identity for the operating workspace and feed that to the prompt, refreshed alongside skills and MCP servers and settled in `beforeSend` so the cached system-prompt prefix stays stable for the turn. An unresolved role now leaves the folder sets undefined rather than empty, so the guidance is dropped instead of claiming there is nothing to write to, and `create_folder` credits the workspace it wrote to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: read the AI provider resource types lazily `Object.keys(AI_PROVIDERS)` at module scope made `AI_PROVIDERS` a load-time requirement for every importer of this module, the global chat included. `AIChatManager.test.ts` mocks `../lib` without it and has been unable to load since the catalog was introduced; no CI workflow runs vitest, so nothing reported it. The constant is read in two places, both inside functions, so deferring it removes the load-time dependency without changing behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ee1f9814c2 |
fix: gate the chat's open_page on the operating workspace's role (#10779)
* fix: gate the chat's open_page on the operating workspace's role Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: don't describe an unresolved open_page role as a denial Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
494e6f146e |
fix: route legacy AI entry points to sessions instead of the unmounted chat (#10705)
* fix: route legacy AI entry points to sessions instead of the unmounted chat Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep createSession's workspace choice and revert pipeline hand-off Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: guard in-session step generation and restore AI action labels Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep AI Fix usable in-session and stop silent no-op hand-offs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: neutral AI form assistant heading to match both branches Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: state the AI form assistant branch rationale once Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: auto-send AI hand-offs and keep in-session step generation in global mode * fix: name the AI session in the entry point labels * fix: claim auto-send reactively and queue programmatic sends mid-turn * test: pin the auto-send claim going stale * fix: stop the script drawer hand-off from abandoning its unsaved script * fix: keep a stale hand-off prompt and close the pre-loading send window * fix: only blank the composer for an intent this wrapper can claim * fix: report composer edits only, never the mount-time draft --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
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> |
||
|
|
ce58b8495c |
feat: expose every runs filter on the open_page chat tool (#10612)
* feat: expose every runs filter on the open_page chat tool Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: reject runs filters the page would silently ignore Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: normalize runs list filters and refuse combinations the page drops Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: validate the full folder-name contract and pin evals to one call Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: refuse queue statuses the concurrency view cannot filter on Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
fc1e11cb3d |
fix: compact ai chat context for models with unknown context windows (#10564)
* fix: compact ai chat context for models with unknown context windows Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: correct stale comment on unknown-model context window handling Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: surface assumed context window in usage indicator for unlisted models Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d9b10e7b0a |
fix(ai): collapse thinking to a status row with a thought-for duration (#10515)
* refactor(ai): render thinking blocks with the shared tool-call card Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(ai): collapse thinking to a status row with a thought-for duration Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(ai): separate reasoning-timing reset from duration read Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai): render expanded thinking in the body font, not mono Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(ai): mark in-progress chat rows with a shimmer sweep Thinking and tool calls both announced themselves with a spinner, which carried no more information than the row already did and read as visual noise once several tools ran in sequence. A white copy of the label now sits over the coloured one and is revealed through a travelling band, so a running row is marked by motion across its own text rather than by a separate glyph. Both spinners and the brain icon are gone, leaving the card with no icon slot at all, and every header label settles on text-secondary. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai): keep a running row marked under reduced motion The shimmer is the only thing distinguishing a running tool row from a settled one, and the reduced-motion rule removed it outright, so the two became identical for those users. The band now degrades to a flat wash instead of disappearing. Also covers the reasoning-duration state machine: that thinking stops at the first answer token rather than at the end of the turn, and that each reasoning pass of a tool-using turn is timed from scratch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(ai): restore the clock spy after the reasoning-duration tests The file-level hook only clears call records, so the Date.now spy stayed installed and would freeze time for anything appended after this block. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
1f912f4104 |
feat(ai): enable data pipelines in AI sessions with alpha notice (#10273)
* feat(ai): enable data pipelines in AI sessions with alpha notice * test(ai): assert real pipeline instructions returned in session * fix(ai): await pipeline editor registration in open_preview to end build_pipeline_node race * test(ai): drop historical narration in pipeline instruction assertion * docs(ai): clarify S3 asset-URI storage forms and literal-key rule in pipeline prompt * docs(ai): document data_upload S3Object input mechanism in pipeline prompt * test(ai): guard open_preview pipeline tool-registration wait seam * feat(ai): report inferred asset lineage in build/edit_pipeline_node results * test(ai): assert open_preview(pipeline) awaits async handler registration * docs(ai): qualify S3Object as wmill.S3Object in pipeline prompt examples * fix(ai): report failure when a backgrounded session's pipeline tools never register * fix(ai): include output_kind-seeded outputs in build_pipeline_node lineage feedback * fix(ai): report only body-detected lineage, never the output_kind seed placeholder * fix(ai): include materialize target in lineage feedback, condense comments * docs(ai): use default-storage s3:/// in canonical pipeline read example |
||
|
|
d6cf1ef987 |
feat: attach text files to chat messages, read on demand via file tools (#10215)
* feat: attach text files to chat messages, read on demand via file tools
* fix: resolve name collisions and sync message files with the transcript
* refactor: render message file chips with the shared context badge
* fix: suffix same-name attachments, sync registry on compaction, bound file bytes
* feat: carry message files across compaction, drop them from the roster
* refactor: merge context, dom and file badges into one wrapping row
* fix: dedupe identical attachments and make badge list keys collision-proof
* fix: name carried files inside the collapsed summary instead of badges
* fix: serialize registry reconciliation and correct the attachment budget
* fix: reserve pending bytes so overlapping reads honor the attachment budget
* fix: share attachment byte budget across concurrent composers
The bottom composer and the edit box are both mounted while editing an
earlier message, but each enforced MAX_CONVERSATION_FILE_BYTES against
only its own staged files plus the transcript. Two attaches near the cap
could each pass independently and overflow the persisted transcript.
Each composer now publishes its staged bytes (committed attachments +
in-flight reads) to the manager, keyed per instance, and the attach-time
budget subtracts every other live composer's stage. A message an open
composer is editing is skipped from the transcript sum since that
composer's stage stands in for it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: charge an edited message at its persisted size until the edit commits
An edit is not committed until send, so the edited message's persisted
attachments return if the edit is cancelled. Substituting only the edit
box's (possibly emptied) stage let the always-mounted bottom composer
claim headroom that vanishes on cancel: remove the near-limit files in
the editor, fill the bottom draft, cancel, and the persisted transcript
overflows MAX_CONVERSATION_FILE_BYTES.
attachmentBytesExcluding now charges a message another composer is
editing at max(persisted size, editor stage), so freed space only
becomes available once the edit actually commits.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: preserve a message file's exact name when a session row clashes on rebuild
syncMessageScoped rebuilds message-scoped rows from the transcript through
collision-suffixing addFiles. Session rows load first (on restore), so one
holding a wanted name pushed the rebuilt message row to a "(2)" suffix while
the persisted prompt still referenced the bare name — get() then resolved the
reference to the session asset and the model read the wrong content.
Free the name from the conflicting session row before the rebuild so the
message row reclaims its exact reference. The rename is in-memory only: it is
deterministic and re-applied on every load, and the session roster is
regenerated live each send, so the session asset stays addressable under the
suffix without a persisted-record update.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: reserve resent files across the edit-resend gap
The edit box unmounts (dropping its staged-byte entry) the instant the user
submits an edit, but restartGeneration then awaits registry sync and beforeSend
before the optimistic bubble lands in the transcript. During that gap the
resent files were reserved nowhere, so the always-mounted bottom composer could
attach into the temporary headroom and the resend would then push the persisted
transcript past MAX_CONVERSATION_FILE_BYTES.
restartGeneration now reserves the resent files' bytes in shared manager state
before the transcript slice; sendRequest releases the reservation once it
installs the bubble (or restores the files to the composer on a pre-install
bail). The reservation bridges the gap so the budget stays honored throughout.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: re-target an in-flight index when a session row is renamed on rebuild
#freeNameForMessageRow renames a restored session row so a same-named message
attachment can reclaim its exact name. But the row's #indexFile, started under
the old name during restore, stamps via #patchFile(oldName, file) — after the
rename that no longer matches, leaving the row stuck 'indexing' so read_file
rejects it and search_files excludes it.
Re-kick #indexFile under the new name when the renamed row is still indexing;
the stale completion then no-ops (its name is gone).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: release resend reservation on local-command sends; surface compaction-orphaned files
Two follow-ups to the message-attachment work:
- A resend edited to /clear or /compact runs the local-command path and returns
before installing a bubble, so the #RESEND_KEY reservation set by
restartGeneration was never released and its bytes stayed charged, blocking
later attachments. Release it on every sendRequest path that exits before
install (via #releaseResendReservation).
- Drop-oldest compaction (summary fallback) removes API messages without a
summary, so a folded message's `## ATTACHED FILES` reference no longer reaches
the model even though the file stays readable. The roster omits message-scoped
files, so the model loses awareness of them. orphanedMessageFileNames() finds
message files whose only referencing message went negative-index, and the
roster now advertises them (summary compaction already carries its own).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: key the resend reservation per send so unrelated sends can't release it
The resend reservation used a single shared key, so a normal or concurrent
sendRequest released it at its own install/early-return even though it didn't
own it — dropping an in-flight resend's reservation and letting attachments
staged before the resend bubble lands under-count against the byte cap.
restartGeneration now mints a per-resend token, reserves under it, and threads
it through sendRequest as resendReservationKey; releases act only on that key.
A send with no token (every normal send) releases nothing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: reserve the whole wanted-name set when freeing session rows on rebuild
Freeing a session row for a message-scoped rebuild suffixed it against current
rows only. With the transcript referencing both notes (2).md and notes.md and a
session row named notes.md, freeing notes.md renamed it onto notes (2).md — also
a wanted reference — so that message row cascaded to notes (3).md while its
persisted reference stayed notes (2).md, and read_file returned the session file.
#uniqueName now accepts a reserved set; the rebuild frees each session clash
clear of the entire wanted-name set, so every message row reclaims its exact
reference regardless of collision order.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: orphan summary-carried files when drop-oldest removes the summary
orphanedMessageFileNames classified summary rows as always-live, but a summary
carries its folded files' reference on its own API message. When summary
compaction succeeds and a later summarization fails, drop-oldest can remove that
API message, yet the files stayed off the roster — so the model lost their
reference even though they remained readable.
The summary display row now tracks its API index (slot 0 at creation, re-based by
drop-oldest); a negative index reads as "counterpart gone" and its files move to
the roster, mirroring user-message orphans. The index is used only for orphan
detection, never as a restart target.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: reserve outgoing file bytes for normal and queued sends too
The resend reservation covered edit/retry, but a normal or queued send has the
same gap: the composer (or queue) clears its files the instant sendRequest is
called, dropping the staged-byte entry, while sendRequest then awaits
regrantLocked()/refreshFolders() before the bubble lands. With a locked or slow
linked folder the composer stays enabled, so a fresh drop can spend the same
headroom and overflow the 5 MB cap once the first bubble installs.
Generalize the reservation: sendRequest mints a per-send token and reserves the
outgoing files' bytes just before attachment upkeep (reusing restartGeneration's
token when present), and releases it on install or any pre-install exit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor: join attachment layers on a stable content-hash id
Sixteen review rounds kept finding the same bug family: a message file's
identity was its display name, joined by hand across the registry, the
transcript, the prompt reference, and the render keys — every same-name
collision or interleaved rebuild made two of those copies disagree.
Give each message attachment a deterministic id, attachedTextFileId(name,
content) — a synchronous pure-JS hash (works on plain-HTTP deployments) whose
exact value is pinned by test, since persisted transcripts reference it. The
prompt and roster list the id, the file tools resolve id-first (bare names
remain a fallback for legacy chats and session links), and pre-id transcripts
hydrate on load by recomputing the same hash — no migration state.
Names become display-only and may collide freely, which deletes the machinery
that defended them: the suffix-readback registration loop, session-row renaming
on rebuild (#freeNameForMessageRow, reserved-set #uniqueName), and the
reconciler's serialization guards (#syncSeq/#syncChain) — syncMessageScoped now
compares ids instead of awaiting blob text, so it is synchronous and cannot
interleave. A same-name clash within one draft gets a composer-local courtesy
rename before the id is minted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: resolve bare names to session rows and scope id searches to one row
A bare name is the roster's namespace: session links are advertised by
filename and have no other handle, so a same-named message attachment (which
is addressed by id) must not shadow them. get() now resolves session rows
first, keeping the message-row name lookup only as the fallback for
transcripts persisted before ids existed.
search_files restricted an id reference by mapping it back to the display
name and letting the worker filter on it — same-named files were then
searched together under one label. The tool now passes the resolved row
itself, so an id-scoped search can only ever hit its own file.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: label search hits with resolvable ids, normalize attach batches at commit
An unscoped search_files reported hits by display name only. Names may
collide, so a hit could not be mapped back to the row that produced it —
a follow-up read_file on the bare name could return a different same-named
file. Rows carrying an id are now labeled `name (file id: …)` in hit lines,
so every hit names the reference that resolves to exactly that row.
addTextFiles normalized (deduped, courtesy-renamed) each file against a
snapshot taken during its read loop. Attach batches overlap, so a file
committed by another batch between reads escaped both checks — duplicate or
same-named unsuffixed entries in one message. Normalization now runs in the
single synchronous commit step against the live list (foldIntoDraft), where
nothing can interleave.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: dedupe renamed re-drops in foldIntoDraft, truncate queued file chips
"Same file dropped twice" means same original (name, content), but a
courtesy rename erases the original name — an identical re-drop then missed
the duplicate check and landed as a further-suffixed copy. The dedupe now
also matches entries whose suffix-stripped base name equals the read's name.
Queued file chip labels get min-w-0 so long filenames truncate inside
max-w-36 instead of overflowing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: address cubic review — line counts, chip clicks, reference robustness
Five fixes from the cubic pass:
- The prompt advertised split('\n').length lines, one more than read_file
reports for newline-terminated files — textLineCount now matches the tool's
numbering (0 for empty, no phantom trailing line).
- Clicking a sent message's badge opened edit mode (the wrapper's
click-to-edit), unmounting the preview popover as it opened; the badge row
now keeps clicks to itself.
- resolve() accepts the composite label rosters and search hits print
(`name (file id: x)`) — models echo references verbatim, so the printed
form must resolve.
- fileToAttachedTextFile enforces MAX_TEXT_FILE_BYTES itself (raw size +
decoded byte length), so no ingestion path can persist an oversized
attachment past the composer's pre-check.
- Duplicate detection after a courtesy rename now uses an explicit sourceName
instead of inferring provenance from the display name — a user's real
`report (2).md` is not a rename of `report.md`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: exact names win over label parsing, commit recheck uses decoded bytes
resolve() parsed any `name (file id: x)`-shaped reference as a printed label,
so a session file literally named that way became unreachable by its exact
name (the dead-id fallback resolved the base name instead). Exact id and
exact-name lookups now run before label interpretation.
Attachment admission and the pending reservation use raw File.size, but the
committed charge is the decoded UTF-8 length — malformed input decodes each
invalid byte to a 3-byte replacement character, so a file passing the 8KB
text sniff could inflate past the conversation cap. The synchronous commit
step now re-checks the live budget against decoded sizes
(admitWithinByteBudget) and drops what no longer fits, with the budget toast.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: normalize files folded into the queued message
Repeated submissions during a stream aggregate into one queued message, but
their files were concatenated raw: an identical re-attach duplicated its chip
and ate a slot (possibly displacing a distinct file at the eight-file cap),
and a same-name clash skipped the courtesy rename. The queue now folds new
files through the same commit normalization as the composer — the queued
entry is a message draft like any other.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* style: compaction boundary label uses text-normal text-2xs
* fix: fold provenance survives pass-through, dequeued files fold into the draft
foldIntoDraft recorded sourceName only for renames it performed itself, so a
file already courtesy-renamed by the composer lost its provenance when folded
into the queue — a later re-attach of the original escaped dedupe. Folds now
compose: the original source name rides through every fold, and dedupe
matches on it.
dequeueMessage restored queued files into a possibly-populated composer by
raw concatenation; prependText now folds them like every other draft
aggregation (dedupe, courtesy rename) before applying the cap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: one MessageDraft owns the lanes that ship with a send
Review rounds kept finding the same P2 shape: an aggregation point where
files join a draft (composer commit, queue append, dequeue restore) that
forgot one of the draft rules — fold dedupe, courtesy rename, slot caps,
byte admission, lanes moving together. The rules existed only as convention
re-implemented per site.
MessageDraft owns them once: text, pastes, images, and text files live on
one object with addFiles (fold + optional decoded-byte admission + cap),
addImages (cap), prepend (restore-merge), replaceIfEmpty (occupied-guard
restore), and take (all lanes leave together). The composer holds a draft
instead of four state vars, and the queue is a draft behind the existing
queuedMessage/queuedImages/queuedFiles accessors — an aggregation point can
no longer skip a rule, because there is no raw array to concatenate into.
Deliberately not moved: @context and DOM picks (ContextManager owns their
lifecycle), the conversation byte budget's cross-composer ledger (store-side
follow-up), and sendRequest's options shape (it decomposes immediately and
is pinned by the manager test suite).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: correct the drop-routing comment, condense the budget doc
* fix: address cubic review — name sanitization, drop hold, merge restores
Four fixes from the cubic pass:
- Attachment display names render into model-facing prompt blocks, and OS
filenames may legally contain control characters — sanitizeAttachmentName
strips them at attach and again at every prompt-render site (legacy names
predate the attach-time pass), so a crafted name cannot inject prompt lines.
- Drop routing awaits handle/entry resolution before it can call
addTextFiles; a send during that window landed the dropped files on the
next message. The drop handler now holds sending (holdSendForIngestion,
taken before the first await) until routing completes.
- Restoring a taken queue after a failed auto-send replaced the queued draft
wholesale, silently losing a follow-up queued during the preflight. Both
#restoreQueue and the unmounted-input requeue now merge via draft.prepend —
the taken entry's text lands above the newer follow-up.
- restartGeneration validated the API restart index only after reserving the
resend bytes and truncating the transcript, so a stale index threw with the
reservation leaked and the display transcript half-mutated. The index is
resolved and validated before anything is touched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: restored drafts keep chronological priority, store names stay resolvable
A failed auto-send's restore folded the taken (older) draft's attachments
AFTER a follow-up queued during preflight, so at the slot caps the older
attachments silently dropped despite the text landing first — and only one
entry's pinned context survived. prepend() now puts the restored lanes ahead
(the cap drops the newest additions) and #restoreQueue unions both pinned
contexts by identity.
Session filenames were sanitized only at prompt render, so an id-less file
whose stored name carries control characters was advertised under a name
that resolve() could not match. Names are now sanitized at every store
row-creation site (attach, folder expansion, refresh, and persisted-row
restore for pre-sanitization records), making the advertised name the stored
name everywhere — render-site sanitization remains as defense in depth.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: update ee-repo-ref to aaa6cb89b05b76139252c64f057e53b94d12ac60
This commit updates the EE repository reference after PR #680 was merged in windmill-ee-private.
Previous ee-repo-ref: 4c08634af953db5c1125b1fb03f5af211fe21db3
New ee-repo-ref: aaa6cb89b05b76139252c64f057e53b94d12ac60
Automated by sync-ee-ref workflow.
* chore: repin ee-repo-ref to main's eb3690a34b (aaa6cb89 needs the unmerged AmqpTrigger OSS companion)
* Revert "chore: repin ee-repo-ref to main's eb3690a34b (aaa6cb89 needs the unmerged AmqpTrigger OSS companion)"
This reverts commit
|
||
|
|
9bc1f62128 |
feat(ai): session chat nits — empty sends, command picker polish, session-state prompt (#10233)
* feat(ai): session chat nits: empty sends, picker polish, session state * fix(ai): scope empty-send turns to global chat and pin new behavior * fix(ai): align grouped search nav with display order, enable empty-send button * fix(ai): fork fallback for unlisted workspaces, section headers across branches * style(ai): hint-colored 3xs picker section headers, drop inline row descriptions * style(ai): more spacing between picker sections * fix(ai): require context elements for empty global-chat sends * style(ai): no empty bubble for text-free messages * fix(ai): review round 2 — keyboard tooltip access, requestedMode guard, no display names in prompt * fix(ai): queue context-only drafts pressed while a response streams * fix(ai): shared context identity for queued badges and full queue-context union |
||
|
|
11fda89b52 |
feat(telemetry): generic feature-usage telemetry with AI session metrics (#10200)
* feat(telemetry): add generic feature_usage table and batched logging endpoint Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(telemetry): log AI session usage events and document them in telemetry settings Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): use escape sequence instead of literal NUL bytes in buffer key Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): validate dimensions, decouple retention, keepalive flush Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): allowlist feature-usage dimensions and index retention scans Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): pin tool-name allowlist and deploy session attribution Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(telemetry): route AI chat usage through feature_usage and drop ai_chat_usage Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(telemetry): slim dimension validation to registered kinds plus key shape Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): backfill ai_chat_usage into feature_usage before dropping it Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): disclose provider and model identifiers in telemetry settings text Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): issue all flush chunks before awaiting so pagehide keeps them Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: update ee-repo-ref to 6306c072a50937ea9af44a5bcf42345543207486 This commit updates the EE repository reference after PR #672 was merged in windmill-ee-private. Previous ee-repo-ref: 964f242a0eb44db7f7d26636cc8d76aeabea2b73 New ee-repo-ref: 6306c072a50937ea9af44a5bcf42345543207486 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
c8870d36ae | fix(sessions): session bar badge readouts, job persistence, refresh bounce (#10217) | ||
|
|
b448af1da7 |
feat(sessions): live DOM access for the raw-app preview in AI sessions (#10129)
* feat(sessions): live DOM access for the raw-app preview in AI sessions Give the session chat read-only access to the rendered raw-app preview, plus inspector-picked element chips synced bidirectionally with the preview. - search_dom / read_dom tools: live, same-origin contentDocument reads by CSS selector (selector omitted = whole body), reusing the file engine over pretty-printed outerHTML (worker-guarded, bounded). Session-gated. - Inspector picks become app_dom_selector context chips (selector-only); the model fetches content on demand. Chips shown even in GLOBAL/session mode. - Multi-select chips synced with the preview: the chip list is the source of truth, pushed to the harness which renders one highlight per selector; add, chip-remove, and preview-× remove all stay in sync. Overlays stripped from search_dom output. The ui_builder harness changes (multi-select highlights + unique nth-of-type selectors) live in windmill-code-ui-builder and ship via the artifact re-pin. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(sessions): inline element prompt + inspector polish for raw-app DOM Session preview: - Add InlineElementPrompt: a floating mini-composer anchored top-left over a selected element; sends a chat turn scoped to that element (its chip is context). Autofocus + remount per selection, borderless input, label gutter so it clears the harness name+size pill. - autosize action gains an optional minHeight (default 30) so the compact inline input renders a tight single line. - Fix exiting select mode: Esc / inspector toggle-off now fully clear the session's DOM-selector chips (source of truth) so the overlays AND the inline prompt are dismissed together, instead of leaving them stranded. Full-page raw-app editor: - Add inspectorHoverOnly: the inspector highlights on hover (outline + name/size) but a click selects nothing — no persistent selection, no app-mode context pick, no inline prompt. Context badges: - ContextElementBadge gains a compact prop (passed only by AIChatMessage): the DOM-selector chip and tagged workspace items (app/script/flow) render smaller above a sent message, while every badge stays the same size in the composer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): address Codex review P1s on raw-app DOM context - Inline element prompt: queue the turn when the chat manager is already streaming (mirror the composer) instead of a concurrent sendRequest that would race the shared abortController / streaming buffers. - DOM selector chips now carry the raw-app path they were picked from. Each preview tab renders/pushes only its own app's chips, and a tab clears cross-app chips when it becomes the active DOM target — a selector could otherwise silently resolve against whichever raw-app tab is active (search_dom/read_dom target the active preview). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(raw-apps): re-pin ui_builder artifact to f79e1c3 Bumps the UI Builder artifact to the release built from ui_builder main after windmill-code-ui-builder#18 merged (the multi-select DOM inspector synced with the host chat). This is the artifact re-pin step that gates merging this PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): inline-prompt vertical centering + auto-collapse DOM tools - InlineElementPrompt: bias the textarea padding (pt-[5px] pb-[3px], same total) so the single line sits centered in the pill — the shared autosize floor otherwise adds slack at the bottom and the text read 1px high. - search_dom / read_dom: drop the explicit `autoCollapseDetails: false` so the tool card collapses after a successful read like other tools (it was copied from get_app_runtime_logs, where staying open is intentional). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): address Codex review on raw-app DOM access - [P1] Release the DOM-requester slot when the active raw-app tab is hidden. Preview tabs stay mounted, so switching to a flow/script/page (or another raw-app) tab left search_dom/read_dom targeting the now-hidden raw app. The slot is now claimed with a per-tab owner token and released via the effect cleanup; the runtime only lets the claiming tab clear it, so a set/release race between two raw-app tabs can't blank the new owner. - [P2] Strip the inspector outline classes from the clone ROOT (the selected element), not just its descendants — querySelectorAll skips the root, so a scoped read of the selected element leaked inspector-picked in its outerHTML. - [P2] Reword the AIChatDisplay context-chip comment to drop drafting-history narration ("now", "not as rows here") per AGENTS.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): scope a queued inline prompt to its own element (Codex P1) An inline element prompt submitted while a turn was streaming got queued as plain text; if the user selected another element before the queue flushed, the queued turn snapshotted the new selection and sent scoped to the wrong element (or none). The queue now carries a context snapshot: - queueMessage(text, context?) pins the selection present at submit time. - sendRequest accepts contextOverride and uses it verbatim as the turn's selected context (feeds both the optimistic bubble and the API message). - All three queue-flush sites thread the snapshot through and restore it if the auto-send bails. - With an override, only the queued message's own DOM chips are consumed from the live selection — a newer selection made since is left intact — preserving the one-shot chip semantics. onInlinePrompt snapshots getSelectedContext() when it queues. (Codex's other P1 — full-page-editor hover-only no longer attaching elements to App AI — is intentional per an explicit product decision, left unchanged.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(raw-apps): restore full-page editor App-AI inspector context Codex/Pi flagged a real regression: passing inspectorHoverOnly disabled the full-page editor's existing App-AI (app-mode) inspector context. Clicking an inspected element no longer set inspectorElement, so the app-mode SelectedContext badge and the element attach in prepareAppUserMessage were dead. inspectorHoverOnly and the session path (onInspectorSelect) are mutually exclusive — the flag was only ever set in the full-page/app-mode context, so it only suppressed the app-mode path. Remove it entirely (prop, guard, and the pass in +page.svelte), restoring the original click -> inspectorElement -> app-mode context behavior. Session behavior is unchanged; the hover highlight still works in both. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): snapshot inline-prompt context synchronously (Codex P1) The immediate inline-prompt path called sendRequest without a context override, so the selected element was read only after the async send preflight (attached-file refresh, beforeSend, global-skill refresh). Picking another element during that window attached the wrong one to the outgoing turn. onInlinePrompt now snapshots the selection synchronously at submit time and passes it as contextOverride on both the queued and immediate paths, so the prompt always rides with the element it was scoped to. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): drop a queued inline prompt's DOM chips when the app changes A queued inline prompt scoped to raw app A described A's elements, but the runtime's single DOM requester targets whichever preview is active. Switching to app B before the queue flushed left the turn describing A while search_dom / read_dom would query B. When a raw-app preview tab becomes active it now also strips the queued turn's DOM chips belonging to other apps (dropQueuedDomContextForOtherApps), so a queued prompt can't ask the model to read one app's selectors against another's live DOM. Non-DOM queued context is preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): scope inline prompt to its anchored element (Codex P1) The inline prompt sits over a single element but snapshotted EVERY selected DOM chip. With several elements selected (Shift-multi-select), a prompt shown over B sent both A and B as context, so "change this button" couldn't identify which one it meant. onInlinePrompt now uses its anchored `selector` argument (previously ignored) to keep only that element's DOM chip in the snapshot; non-DOM context is preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): preserve original context on edit/retry (Codex P1) restartGeneration resent with the live selection, so editing or retrying an element-scoped prompt lost its DOM selector (or adopted a newer one) — DOM chips are one-shot and cleared from the live selection after the first send. It now passes userMessage.contextElements as contextOverride, re-using the exact context the message was originally sent with. undefined (modes that don't attach contextElements) falls back to the live selection as before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(sessions): route search_dom/read_dom to the selector's own app preview The DOM requester was a single active-tab slot, so search_dom/read_dom always read whichever preview was visible. A turn scoped to app A that read the DOM after the user switched to app B would silently read B (Codex P1). Preview tabs stay MOUNTED when hidden, so app A's DOM is still there — the fix is to route by app instead of "active tab": - Every mounted RawAppEditorView registers its DOM requester keyed by its app path (runtime holds a map, not one slot); the visible tab is tracked separately as the default target. - search_dom / read_dom gain an `app_path` (surfaced per chip in the SELECTED DOM ELEMENTS block and the tool schema); the runtime routes the query to that app's still-mounted preview. No app_path → the active preview, or the only one open. - If the named app's preview has been closed, the tool returns "The preview for X is no longer open…" so the chat can explain it, rather than reading the wrong app. This makes the queued-context drop-on-switch (dropQueuedDomContextForOtherApps) redundant — removed. The lightweight submit-time snapshot (which element the message is about) is kept. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): address Codex review on DOM access (script strip + app-scoped chips) - search_dom/read_dom: strip the app's compiled <script> bundle from the whole-body clone so queries only see rendered HTML, not source (P1). - DOM chip add-dedup and removal now match both selector AND app path, so an identical selector in another app can't drop or block a chip (P2). - Refresh stale single-slot requester comments to describe per-app routing; reword the foreign-chip reset to reflect why it stays (composer chips are unlabeled by app) (P2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): scope DOM-chip clear to its app so a rebuild can't wipe another (Codex P2) Every mounted preview emits inspectorClear on rebuild, routed to onInspectorClearAll → clearSelectedDomElements(). Unscoped, a hidden app A rebuilding cleared app B's active chip and highlight. clearSelectedDomElements now takes an optional appPath; the preview-clear path passes its own path, while post-send and foreign-reset clears stay unscoped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): reject a <script>-root DOM query so the app bundle can't leak (Codex P2) The descendant strip (querySelectorAll('script')) skips the clone root, so a search_dom / read_dom query whose selector targets `script` serialized the whole compiled bundle. Reject a script root with an explanatory result before cloning. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): union queued inline-prompt DOM context + reject inspector-label root (Codex) - Multiple inline prompts queued during one stream accumulate their text, but queuedContext replaced (last wins) — dropping an earlier element's chip and misapplying its instruction. Union the DOM selector chips across queued prompts (non-DOM context still from the latest snapshot) (P1). - search_dom/read_dom: reject an .inspector-label clone root, like the <script> root — the descendant strip skips the root, so a `.inspector-label` query would return inspector chrome (P2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): restore consumed DOM chips when a turn is cancelled unsent (Codex P1) DOM selector chips are consumed from the live selection before the request goes out. If the user hits Stop before any usable output, restoreUnsentTurn put the text/pastes/images back but not the chips, so resending the restored prompt lost its element scope. Re-add the consumed chips on rollback (skipped on a queued- message handoff, which carries its own context). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): edit box edits the message's own context, sends what it shows (Codex P1) Editing a past message bound the edit box to the LIVE selection while the resend carried the message's original contextElements — so the box showed one set of chips (or none) but sent another, and add/remove in the box did nothing. The edit box now edits a copy seeded from the message's own contextElements, and restartGeneration sends that edited copy; a bare retry still falls back to the original. Drops the now-unused selectedContext prop from AIChatMessage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): keep DOM context coherent on dequeue/cancel + per-chip edit identity (Codex) - dequeueMessage dropped the queued draft's pinned DOM context, so returning it to the composer sent the live selection instead. Restore the draft's context on dequeue (P1). - Cancel-rollback re-added this turn's chips additively, mixing in chips selected mid-stream. Replace instead so the restored draft stays coherent — shared helper #restoreDomContext used by both paths (P1). - The context chip row keyed/removed by (type, title); repeated DOM elements share a title (two button.btn), so editing a multi-select message gave duplicate keys and deleting one chip removed both. Identify DOM chips by (appPath, selector) (P1). The queued-prompt instruction↔element association (Codex #1) is the accepted union tradeoff and is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): scope read_dom pagination + don't consume live chips on edit/retry Codex P1s: - read_dom's continuation note only renamed read_file, so following it with start_line alone dropped app_path/selector and re-read the active app's whole body instead of continuing inside the element. - contextOverride also carries an edit/retry's replayed copy of an older message's context. Consuming it from the live ContextManager stripped an identical chip the user had since selected in the composer, leaving their draft unscoped. Overrides now declare their origin ('pinned' vs 'replay'). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): keep DOM context paired with the draft it belongs to Restoring a draft's pinned DOM chips replaced the live selection unconditionally, but both restore paths can leave a different draft in the composer: - Cancel: an occupied composer declines the rolled-back text (restoreInstructions bails), yet the cancelled turn's chips still replaced the live ones — the draft the user typed during the stream kept its text but got retargeted. - Dequeue: queued text is prepended onto an existing draft, so both instructions share one composer; replacing the chips dropped the standing draft's element. restoreInstructions/prependText now report whether the composer took the text. Cancel restores context only when it did; dequeue keeps both drafts' chips when it merged into one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sessions): scope an inline prompt's chip filter to its own app The filter narrowing a multi-select down to the anchored element matched on selector alone. Selectors are generated per app and collide across them, so an inline prompt in app B also carried app A's identically-named chip, leaving the model two indistinguishable referents. Since draft restoration can now legitimately hold chips from several apps, match (selector, appPath) — as the sibling deselect/clear handlers in this file already do. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7a139ab23e |
feat(ai-chat): image attachments and agent raw-app screenshots (#10130)
* feat(ai-chat): add image attachments and agent raw-app screenshots Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(ai-chat): generalise take_screenshot fidelity caveat Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): keep compaction boundary on a displayed user message Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(raw-apps): count line boxes by vertical overlap, not rect count Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): enforce vision gating and bound image attachments Refuse images on known text-only models instead of warning and sending them anyway; cap input bytes before decode; keep clipboard text when it accompanies a bitmap; don't queue a message whose images can't ride the plain-text queue. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * perf(ai-chat): trim take_screenshot schema and shrink the card's copy Move the fidelity caveat from the tool def onto the tool result: the def is re-sent every global iteration (~258 tok), while the caveat only matters once a capture exists. Keep a downscaled copy in displayMessages when it is actually smaller — those are never compacted and are re-cloned on every saveChat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): carry attached images through the message queue Enter during a streaming turn queued the text and silently dropped the images, so the auto-send was not the message the user submitted. The queue now holds both, moved together via takeQueue/clearQueue/restoreQueue so none of the three flush sites, the dequeue-to-composer path, or the two conversation-switch drops can leak one without the other. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): gate screenshots on vision, narrow when the tool fires take_screenshot buffered an image unconditionally, so a text-only model got an image_url and rejected the turn; the attach-time check never covered it, nor a model switched after attaching. Gate before capture and again at send. Only reach for the tool when the user raises how the app looks, rather than after every UI edit. A collapsed preview keeps the iframe mounted at zero width, passing the ready checks and then failing inside the rasteriser as '[object Event]'. Name it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): hold sending while attachments decode addImages read the free-slot count before its await and appended after it, so a send during the ~50-800ms decode cleared images while the closure still wrote to them, landing the picture on the following message; two drops also claimed the same slots and could pass the cap. Reserve slots up front, block sending until they resolve, and show a placeholder so the held send is explained. Keep only a bounded copy in the transcript: displayMessages are never compacted and are re-cloned on every save. Measured 6.1x smaller per attachment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): route screenshots to the visible tab, resend full-res on retry Every mounted raw-app editor claimed the runtime's single screenshot slot, so take_screenshot could capture a background tab's app; ownership now follows the visible tab and only the owner releases it. restartGeneration resent displayMessages' images, which became a 384px thumbnail when the transcript copy was bounded — retries downgraded the model's own input. Recover the sent parts from the API message instead. Move modelSupportsVision to modelConfig: it was untestable behind lib.ts's monaco import chain, and the denylist missed bundled text-only defaults (Groq/Together Llama 3.3, Foundry Phi-4 and Mistral-Large). Llama 3.2 and Phi-4 split by variant, so both are matched narrowly. Pinned against the shipped defaultModels. Decode attachments one at a time and derive the preview from the bounded copy: a 12MP bitmap is ~48MB and the batch was held live at once, decoded twice each. The attach tooltip claimed nothing is uploaded, which is untrue for images. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): keep images out of text-only turns and bound the queue The vision gate only dropped the current turn's images, so history's image parts still went out after a switch to a text-only model and failed the request; strip the outbound copy instead, leaving history intact for a switch back. queueMessage had no cap, and each queued send clears the composer for another eight, so repeated sends stacked an unbounded batch into one message. Editing a message resent displayMessages' bounded copy, downgrading the model's own input; retries recovered the full-size one but then re-persisted it at full resolution. storedImages pairs the API message with its transcript entry so both paths resend the original and re-persist the bounded copy. Reserve image slots before awaiting text attachments: the gap left sending enabled with an image pending, measured ~90ms for a 40-file drop, now ~8ms regardless of batch size. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): treat deepseek-v4 as text-only deepseek-v4-pro ships as a bundled default and the gate let images through to it, so an attachment would fail the turn. DeepSeek's vision line is deepseek-vl. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): drop a rejected image instead of wedging the conversation A provider that refuses an image leaves it in history, so every later turn resends it and fails identically: the chat is stuck until the user edits the message or starts over, and Retry re-sends the same image. The vision gate only knows the models we ship, so this is the net for the rest. Strip the parts on an image-related rejection and say so; unrelated failures keep the image. Verified at the wire that no provider rejects a base64 data URL: anthropic (source.base64), openai/gpt-4o (input_image), googleai and aws_bedrock/claude (image_url passthrough) all 200 and read the image. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): match text-only models exactly, from provider API docs The gate guessed by substring over model names, which answers the wrong question. What matters is whether a provider's API accepts image parts, not whether the model can see: DeepSeek V4 ships vision in its chat product that its API has no content type for, and o3-mini gained vision in ChatGPT the API never exposed. Neither is inferable from a name. Substrings also block working models. 'mistral-large' matches Mistral Large 3, which takes images; 'phi-4' matches Phi-4-multimodal, which does too. A wrong entry blocks with no override, while a missing one costs a turn and recovers via the rejection path, so the list is now exact ids only, each backed by a provider doc. Verdicts verified against provider API docs rather than recall. Live-checked where a doc was contradicted: Bedrock's compatibility matrix claims no Anthropic model is served over chat completions, but it serves images fine. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): stop retry resurrecting a rejected image The rejection fallback strips the image from history but leaves the bubble's thumbnail so the user can still see what they sent. storedImages fell back to that thumbnail when the API message had no parts, so Retry re-attached the very image the provider had just refused and failed identically — the conversation stayed wedged through the one control offered to escape it. Found by retrying in the UI; unit tests, wire tests and four review passes all missed it, since it only exists between two separate fixes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ai-chat): harden image rejection recovery and drop-path attachment * fix(ai-chat): fix image drop race, mid-turn vision gate, retry aliasing * fix(ai-chat): key vision denylist by provider, flatten alpha before jpeg * feat(ai-chat): offer take_screenshot on chromium only, ask for one elsewhere * feat(ai-chat): image-only sends and click-to-expand image previews * fix(ai-chat): capture screenshots at 2x and expand tool images full-res * feat(frontend): expandable image previews in composer and result views * fix(ai-chat): image-only send edge cases from review round * fix(ai-chat): keep image-only drafts on rollback, track failing model id * fix(ai-chat): gate rejection recovery on the failing iteration's model * refactor(ai-chat): record iteration model via onBeforeIteration, trim tests Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): restore composer draft when beforeSend preflight fails Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): bound cumulative outbound image bytes per request Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): make the image byte bound part-granular so over-cap turns keep a subset Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): evict newest-first within a message in the image byte bound Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): prune over-cap images from stored history, not just requests Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): bound history at every save boundary, keep thumbnail pairing across eviction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): slot-align storedImages so the bubble expands the right image after eviction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): match rejection keywords as whole words so provisioning errors keep images Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): match input_image rejections, restore images refused by non-GLOBAL modes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): defer non-GLOBAL image refusal restore past the composer clear Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): persist full tool screenshots for post-reload expansion Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(ai-chat): persist chat images out-of-band via blob-store refs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): scope image blobs per chat and stop cap-eviction rotation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): keep blob-cap chronology across drop-oldest compaction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(ai-chat): derive blob eviction from the saved record, not write times Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): serialize chat history DB writes per manager Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): pin queued history writes to the enqueue-time user database Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): delete stale image blobs only after the chat record commits Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): don't double-restore a queued image-only draft on vision refusal Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): label image-only chats and evicted image-only bubbles Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): keep the in-memory chat mirror hydrated for DB-less sessions Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): converge the chat mirror to refs after a successful DB commit Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): guard mirror convergence against rewinding newer saves Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): invalidate pending convergences on identity re-init, keep retry image names Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai-chat): bound the screenshot raster before rasterization Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(ai-chat): drop the no-IndexedDB in-memory image fallback Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
a9fc9f74b2 |
fix(ai): unbreak session chat compaction for Anthropic models (#10171)
* fix(ai): cap chat compaction summary output so Anthropic non-streaming calls succeed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: pin the compaction summarizer's maxTokensCap Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ai): cap testKey completion so Anthropic key tests pass the SDK pre-flight Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
286da005ef |
feat: AI chat background jobs tray with detach, approval and preview (#9982)
* feat(ai-chat): background jobs tray with detach, approval and preview Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): route exec_datatable_sql through the jobs tray Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): jobs tray — orange queued badge, 5-recent pagination, drop remove button Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): silence dev-only false-positive binding warnings Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sessions): silence dev-only false-positive binding warning in FlowEditorView Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): auto-expand jobs tray on approval, close modal on resume Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): let the AI set a per-call inline wait before jobs detach Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): auto-resume the chat when a background job finishes while idle Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): merge jobs tray and edits bar into a segmented session bar Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): address review — canceled-job handling, cross-chat poll guard, tests Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): gray chip dot for canceled-only jobs instead of green Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): keep jobs segment right-aligned when there are no edits Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): address /review — drain snapshot, live region, a11y, leading-ellipsis, remove dev harness Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): announce all same-tick job completions; drop redundant aria-live Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): guard poller re-entrancy; datatable error fallback (auto-review P2/nit) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): honor tool formatter on detached job completion; coalesce poller Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): persist tool result formatter so rehydrated detached jobs keep contract Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7046dc6dfb |
fix(sessions): scope fork session Edits to session-edited items only (#9989)
* fix(sessions): scope fork session Edits to session-edited items only A session chat with an undefined modified-items mask fell back to showing every draft in its (possibly forked) workspace, so the Edits bar/diff drawer listed all fork drafts instead of just what the session edited. Always track session chats: seed an empty mask for legacy chats in loadPastChat and guard the not-yet-persisted-chat case in initRuntime. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: clarify session chats always persist their modified-items mask Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a6c0b3756b |
feat: chat-scoped session changes bar + unified diff drawer (#9762)
* feat(frontend): chat-scoped unified session changes bar Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(frontend): drop diff-baseline toggle, show natural per-row diffs Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): carry Draft marker to expanded raw-app file rows Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): show raw-app Draft badge once at tree root, not per file Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(frontend): reuse shared DraftBadge in session diff drawer Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): show draft-author avatars in session diff badge, icon-only in sidebar Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): keep badge pill around avatar in icon-only DraftBadge Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): small draft marker = indigo pen + avatar; correct itemKind label Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(frontend): drop package-lock churn from merge (match origin/main) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): dedup diff-button count for legacy fork sessions; test mask helper Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): collapsible session diff panel + per-row open-diff action Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): shared sessionDeployModel for review & deploy (S1) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): model-driven session review drawer, deploy inert (S2) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): wire session deploy + on-behalf/conflict gating (S3) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): behind banner + Update fork + deployment request (S4) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): session changes dock opens drawer by filter (S5) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): session review UI polish (badge, dock, In parent, tree width) - draft rows show only the avatar DraftBadge, not a duplicate state pill - drop redundant dock Review button (same as "N to review") - rename Done -> In parent with a "deployed in parent workspace" tooltip - widen the file tree Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): existence-gate In parent rows; Badge filters; badge hover - drop discarded mask-only items from the In-parent segment (existence check) - use the Badge component for the drawer filter segments and the changes dock - soften the blue Badge hover (blue-50 base was jumping to blue-200) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): polish session diff drawer (layout, badges, actions) - remove empty fork-banner gap; uniform sidebar tree padding - full-bleed diff list: drop card borders/side padding, separators between items - clamp tree x-overflow; right-align tree badges (min-w-0 on the row button) - brand-compliant selected filter badges; smaller draft badge - hide per-row open-diff button when the panel is open - rename "Delete draft" to "Discard draft" (destructive); remove header Review button - larger sm deploy/discard action buttons; remove per-item diff-content collapse Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): session Edits dock — deploy gating + change-op tracking Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): session bar per-status badges; drop change-op tracking Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): unwrap raw apps into per-file tree in session diff sidebar Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): dot-parcours pipeline (badge-derived, melt tooltip) + discard confirm Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): behind-only session item reads as deployed, not bare Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * style(frontend): even sidebar tree margins; gutter-aware right padding Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): pass chat id as from_session; wire deploying flag Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): session diff drawer scroll-to-flush, ordering, spacer, deploy gating Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): preserve chat mask on compact; guard stale existence checks; clear poll timers Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): refresh bar after drawer deploys; conflict hint over chip; plain conflict badge Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * style(frontend): diff drawer card layout with flash ring and aligned insets Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): hide stale deployed chip once row status badge reads deployed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(frontend): session dock to two states; drop parent deploy Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): staged deploy animation in session edits drawer Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): stale-draft warning in session edits drawer Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): keep chat mask honest on deploy and discard Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): map trigger_email deploy kind; serialize mask persists Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): reset mask on new chat; close review-flagged races Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * style(frontend): rename session drawer title to Edited during session Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): keep mask persist queue alive after a failed save Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): sync session chatId on chat rotation; gate deploy on canWrite Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): keep compare handoff for deletion-only session edits Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): hold deploy success beat across re-keyed rows Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3be27521b0 |
feat(ai-chat): let global chat edit the user's personal instructions (#9771)
Add an update_user_instructions tool to the global-mode AI chat so the user can ask it to remember a preference or change/stop a behavior, and it persists the change to the user-level Global custom prompt. - update_user_instructions tool: append a new instruction, or find/replace to edit/remove existing text (reuses the shared findAndReplace helper); enforces the 5000-char cap and echoes current text on a failed match. - GlobalToolHelpers gains getUserInstructions/setUserInstructions; the manager wires them to the localStorage user-prompt store and rebuilds the system message so the change applies on the next chat-loop iteration. - Render workspace vs user instructions under distinct headers in the global system prompt (getCustomPromptParts) so only the user block is presented as editable. - Keep the tool result lean: return a short confirmation, not the full instructions (already re-injected into the system prompt next turn). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3fafac275d |
feat(ai-chat): add /clear session command to start a fresh conversation (#9769)
* feat(ai-chat): add /clear session command to start a fresh conversation Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): don't re-queue a built-in command flushed from the queue Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
83cc5533ee |
feat: add /compact session chat command (#9764)
* feat: add session chat slash commands * feat: add /compact session chat command Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: dedupe built-in commands against same-named workspace skills Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
24b95e9fe1 | feat: add session chat slash commands (#9748) | ||
|
|
6f4017d694 |
feat(ai-chat): workspace AI chat skills (SKILL.md upload + read_skill tool) (#9648)
* feat(ai-chat): workspace ai_skill table + CRUD API Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): AI Skills workspace settings tab with SKILL.md upload Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): advertise skills in global system prompt + read_skill tool Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ai-chat): move custom skills into AI settings (paste or folder) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): cap folder import (depth<=3, max 50 skills, confirm dialog) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style(ai-chat): give import folder its own labeled subsection Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): resolve svelte-check never-narrowing in skills preview Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address ai skills review issues * fix: validate ai skills and reload workspace list * fix(ai-chat): spec-align skill validation and cap skills per workspace Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): reject duplicate skill uploads, audit skill names Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): sync deref openapi specs with skill validation rules Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
84cc043406 |
feat: link files & folders to the global AI chat (#9520)
* feat: add file attachments to the global AI chat Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: add folder linking and file-type icons to chat attachments Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: persist linked files, add @-menu file tree, and polish chat file UI Persistence (survive reload, scoped to session.id): - IndexedDB store (attachedFilesDB) holding Blob snapshots (every browser) and re-grantable File System Access directory handles (capable browsers) - restore on session activation; re-grant locked handles on the next send; flush in-memory items when the session persists; GC on session delete - capability via feature-detection (fsAccess), never UA sniffing - folders auto-refresh (live re-enumerate + reconcile) on each send @-mention file picker: - Files branch in ChatContextPicker (new DrillPicker architecture); a linked folder's files render as a nested directory tree, picking inserts @filename - attached-file mentions highlight in the input just like context mentions UI polish: - file/folder chips reuse the context-element chip style (icon -> X on hover) - file + context badges sit above the fork/draft bar - disabled dropdown items can surface an explanatory tooltip (DropdownV2Inner) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor: deepen the attached-files store — folders as first-class objects Two seam fixes from an architecture pass, no behaviour change: - addFolder(dirHandle) now enumerates internally (same junk-filtered walk used on restore/refresh), so callers never pre-enumerate. The dead drop-walkers (collectDroppedEntries, filterFolderPickerFiles) are deleted; isIgnoredPath/MAX_FOLDER_FILES move next to enumerateDir in fsAccess. - The store exposes `folders` (name + aggregate status + children) and `standalone` as derived views, so the bar, the @-menu picker, the folder chip and the system-prompt roster stop re-grouping the flat row list and re-deriving folder status. Placeholder rows (isFolderRoot) become an implementation detail; the roster renders a locked folder as one line. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: drop the redundant context-badge row in the global chat In GLOBAL mode selected context already appears as a highlighted @mention in the input (deleting the mention deselects), so the hoisted badge row above the chat duplicated it. File chips keep their row — attachments aren't represented in the input. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: harden attachment edge cases found in review - requestReadPermission/queryReadPermission never reject (the spec rejects with SecurityError when user activation is missing — now mapped to denied/prompt), and sendRequest wraps attachment upkeep in try/catch, so a permission hiccup can never silently swallow a Send. - regrantLocked expands before dropping the locked placeholder: when the re-granted directory is gone from disk, the folder now shows "unavailable" instead of vanishing into a zombie that resurrects locked on the next reload. - addFolder: re-picking a locked/unavailable folder relinks it (natural recovery gesture); a genuine second folder with the same basename gets a visible "already linked" rejection instead of a silent no-op. - fileEngine: readFile clamps its byte slice to maxChars*4 before decoding and streamLines caps its per-line buffer, so newline-sparse files (minified JS, single-line JSONL) can't materialize unbounded strings; corrected the scan-cap comment's claim about catastrophic backtracking. 4 new unit tests (41 total). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: surface folder-picker failures instead of swallowing them `pickDirectory` caught every `showDirectoryPicker` rejection and returned undefined, so a real failure (an enterprise/browser policy blocking the File System Access API, a lost user-activation, …) was indistinguishable from a no-op — the picker just silently never opened. Now only `AbortError` (user dismissed the dialog, or CDP intercepted it under automation) is treated as a cancel; anything else is rethrown and `linkFolder` surfaces it as a toast. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: support folders in browsers without the File System Access API Folders can now be added in every browser, not just Chromium. Where the File System Access API is absent (Firefox/Safari), a dropped or picked folder's files are snapshotted into the browser (via a webkitGetAsEntry drop-walk or a `webkitdirectory` input) instead of linked as a live handle, and grouped/displayed identically to a File System Access folder. The dropdown item reads "Link folder" when a live link is possible and "Add folder" otherwise, with a tooltip pointing to Chrome/Edge for a live link. Snapshot folder children persist their `folder`/`relPath`, so they regroup into the same folder chip on reload. Removes the arbitrary file-count caps (500 per folder, 100 total) — only the browser's memory / IndexedDB quota now bound a folder. Junk paths (node_modules/.git/dist/dotfiles) are still skipped, folder-contents only, so an explicitly attached standalone dotfile is kept. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address review feedback — index-race guard + read_file line numbers Both automated reviewers flagged two issues on the attached-files feature: - (P1) Stale async indexing could corrupt a newer file. `#indexFile` applied its unawaited `buildLineIndex` result by display name, so if a row's file was swapped while indexing was in flight (remove + re-add a same-named file, or a folder refresh re-indexing an edited file) the stale result stamped the wrong lineIndex/lineCount — and `read_file` then sliced the new Blob with old offsets. Now patched via `#patchFile`, which applies the result only while the row still holds the exact file object that was indexed. - (P2) `read_file` promised "line-numbered context" but returned raw text. It now prefixes each line with its absolute 1-based number (`<n>→<content>`), matching the tool contract; `numberLines` lives in fileEngine and is unit-tested. Adds regression tests: a deterministic stale-index race test (controlled buildLineIndex ordering) and numberLines coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address re-review nits — read_file pagination + searchFiles regex state - read_file: when the maxChars cap truncated a window short of its requested end line, the pagination note still reported the full range and gave no/wrong resume point, so the model couldn't reach the unread lines. The note now reports the last line actually returned and resumes at the next unread line (advancing past a single over-long line rather than re-truncating it forever). - searchFiles: reset `regex.lastIndex` before each `.test()` — a caller-supplied `g`/`y` flag makes test() stateful and would silently drop matches. Not reachable from the current caller, but searchFiles is exported. Adds regression tests for both. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep an emptied live folder linked and refreshing A live (File System Access) folder carried its directory handle only on its child file rows. When the folder was emptied on disk, refreshFolders/#reconcileFolder removed the last child — dropping the only handle-bearing row — so the folder vanished from the chip bar AND was never re-enumerated again (files added back on disk weren't picked up until a reload). #expandFolder had the same gap on restore. Now #ensureFolderRow leaves one handle-carrying placeholder row when a folder has no readable children (keeps the chip visible and the live source alive), and drops it once children return; refreshFolders collects sources from placeholder rows too, and readyFiles never exposes a placeholder to the read/search tools. Adds a regression test (empty → still visible → file returns → picked up). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: trim read_file char-cap output to match its pagination note When the char cap cut partway into the line after some whole lines, readFile set the note/endLine to the last complete line but still returned the partial next line in `text` — so read_file showed (line-numbered) a line the note said would come on the next read. Trim the returned text back to the last complete newline so the body and the note agree. Test now asserts res.text for that case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: isolate search_files in a Worker (ReDoS) + path-aware folder dedup - search_files runs a model-supplied regex, and a catastrophic-backtracking pattern (e.g. /^(a+)+$/) can't be interrupted mid-test, freezing the tab. Run the search in a Web Worker (searchFilesInWorker) and terminate it on a timeout, returning "pattern too expensive" instead of hanging. Degrades gracefully to a main-thread search where Workers are unavailable / fail to load. - #isDuplicate keyed its content check on the file basename, so two distinct files sharing a basename under different folder subdirs (proj/a/index.ts vs proj/b/index.ts) were wrongly deduped and silently dropped from snapshotted folders. Key it on the relative path instead. Adds tests: worker result/timeout-and-terminate, and same-basename-different-subdir. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep an initially-empty live folder linked (placeholder + persist) addFolder only created rows / persisted the dir-handle when at least one text file was found, so linking a folder that's empty (or all-binary) at pick time was a silent no-op: no chip, nothing persisted, and refreshFolders had no source to re-enumerate when files were added later. Now it always leaves a placeholder (#ensureFolderRow) and persists the handle — matching the became-empty behavior — so the folder stays visible, survives reload, and picks up files added afterward. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep empty-folder placeholders out of the real-file name space The placeholder row for an empty live folder uses name = folder, which could collide with a standalone file of the same name: addFiles deduped the file against the placeholder, removeFile(name) dropped both rows, and #uniqueName pushed the file to a "(2)" suffix. Placeholders are managed via removeFolder and never read by the tools, so exclude isFolderRoot rows from #isDuplicate, removeFile, get(), and #uniqueName. Adds a placeholder/standalone collision test. (codex's other nit — @-mentions not highlighting filenames with spaces — left as a known cosmetic limitation per the chosen scope.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: highlight @-mentions of filenames containing spaces A file mention was inserted verbatim as `@my file.txt`, but the highlighter regex `@[\w/.\-\[\]]+` stops at the space, so only `@my` was parsed/highlighted and the mention didn't behave as advertised. Introduce a small shared `mention` module: names with whitespace are inserted in a bracketed form `@[my file.txt]`, and the shared regex + `mentionTitle` parse both bare and bracketed tokens. Both insertion entry points (the inline `@` picker in ContextTextarea and the toolbar path in AIChatInput) now use `formatMention`, so the full name highlights. Verified in a real browser: `@[my file.txt]` renders as a single highlight span. Unit tests cover format/parse/round-trip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: search_files reports a requested file's real status, not "not attached" search_files filtered the store down to readyFiles() before validating a requested `file`, so searching an attached-but-not-ready file (indexing / errored / locked / unavailable) while another file was ready returned "No attached file named X" — even though it is attached. Factor read_file's status reporting into a shared notReadyMessage() and have search_files report the same accurate status before searching the ready subset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: clear attached files on new/loaded chat in the non-session global chat saveAndClear() (the "New chat" button) and loadPastChat() left attachedFiles intact. In an AI session that's intended — files are session-scoped and persist across conversations. But the ephemeral global side-panel chat has no session, so the next, unrelated conversation still got the previous file roster injected and could read_file/search_files against it. Clear attachments on both transitions when `!isSessionChat`; sessions keep them. Adds a lifecycle regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep an empty folder linked when regranting access after reload regrantLocked() dropped the locked placeholder unconditionally after #expandFolder. If the regranted folder was empty (or all-binary), #expandFolder's #ensureFolderRow no-op'd (the locked placeholder still existed), so dropping it removed the only handle-bearing row — unlinking the folder and stopping future refreshFolders from ever seeing files added back. Re-ensure a ready placeholder after dropping the locked one. Adds a regression test for the empty-regrant path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: round-trip @-mentions of filenames containing a closing bracket The bracketed mention form `@[name]` broke when the name contained a `]` (e.g. `notes ] draft.md`): the regex stopped at the first `]` and mentionTitle resolved the wrong name, so it wouldn't highlight. Escape `\` and `]` when bracketing, match escaped chars in MENTION_RE, and unescape in mentionTitle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: highlight @-mentions of filenames with HTML-sensitive / special chars getHighlightedText() escapes the textarea value to HTML before parsing mentions, then looked the parsed title up against raw attached names — so a file like `R&D notes.md` (escaped to `R&D notes.md`) never matched and wasn't highlighted. Also, names with chars outside the bare set (`<`, `>`, `&`, parens, …) weren't bracketed, so the bare regex truncated them. Now formatMention brackets any non-bare-safe name, and the highlighter HTML-unescapes the parsed title before the store lookup. Verified in a real browser with `R&D notes.md`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: report the real reason search_files has no readable targets When attachments existed but readyFiles() was empty, search_files always told the model "still being indexed, try again shortly". That's wrong for the placeholder states this PR introduces: an empty or binary-only linked folder leaves only a filtered-out `ready` placeholder, and a locked/unavailable restored folder exposes no readable children. Now the message reflects the actual state — no searchable text, restore access, or re-link — and only says "indexing" when something is. Adds a focused fileTools test for the empty-ready states. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
5d553b81c0 |
feat(ai-chat): summary-based conversation compaction (#9645)
* feat(ai-chat): summary-based conversation compaction Replace drop-oldest compaction with summary-based partial compaction: when a send would cross the context-window trigger, summarize the older prefix into one message and keep the recent tail verbatim, replacing the prefix in both the model context and the visible transcript with a collapsible boundary. Drop-oldest remains a fallback; a circuit breaker disables the summary round-trip after repeated failures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * nit * fix(ai-chat): address review findings on summary compaction - Stop during an in-flight summary no longer falls through to a destructive drop-oldest compaction. The aborted controller short-circuits the fallback and its save, so the cancel path rolls the unsent turn back cleanly instead of permanently dropping older history (P1). - Preserve the original chat title across compaction: once the summary boundary leads the transcript, reuse the title computed before compaction rather than re-deriving it from the first surviving tail message (P2). - Strip every <analysis> block from the model's summary, not just the first, so extra scratchpad blocks can't leak into context (P2). - Reindent AIChatMessage.svelte / ContextUsageIndicator.svelte (prettier). Adds regression tests for the abort path, title preservation, and multi-analysis stripping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * nit --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
51bd8692a4 |
feat: queue messages typed while ai chat is streaming (#9525)
* feat(frontend): queue messages typed while ai chat is streaming * fix(frontend): avoid losing queued chat messages on send early-return * test(frontend): cover queued chat message semantics in AIChatManager * fix(frontend): complete ChatLoopResult mock in queued message tests * feat(frontend): single appendable queued message, send on cancel * fix(frontend): only auto-send queued message on a user cancel, not programmatic * chore(frontend): remove queued-message dev preview page * fix(frontend): clear queued chat message on conversation switch |
||
|
|
aa26c4d9b2 |
feat: scope AI session storage per user, session list in IndexedDB (#9518)
* feat: scope AI session browser storage to the logged-in user Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor: address review nits in user-scoped session storage Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor: store AI session list in per-user IndexedDB via shared userScopedDb Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: cover legacy chat-history migration; address review nits Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: harden chat-DB writes and session-key cleanup per PR review - route HistoryManager writes through whenReady() so they can't land in a previous user's DB after an in-place user switch (drop cached this.indexDB) - dedup the legacy chat-DB claim to one session-wide promise so racing manager instances can't issue blocking concurrent deleteDB calls - delete bare windmill_sessions keys unconditionally even when the user DB is already populated, closing a partial-migration leak window Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: trim package-lock to only the fake-indexeddb addition npm install -D had also stripped "dev": true from ~34 unrelated optional native-binding packages (npm graph recomputation). Restore main's lockfile and graft in only the fake-indexeddb node, so the lock diff is exactly the intended dev dependency and prod-install classification of those bindings is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2b471805bf |
feat(frontend): precise AI chat context usage tracking + indicator (#9551)
* feat: track real ai chat token usage and show context indicator * fix: keep context anchor full-history accurate after trimmed sends * nits * feat: restyle context indicator and disable trim for unknown windows * feat: hide context indicator below 50% usage when window is known * fix: gate 1M claude context window to sonnet/opus 4.6+ * refactor: import context window helpers from modelConfig directly * fix: keep base gpt-5 models at 400k context window * fix: exclude date-suffixed claude 4 ids from 1M window gate * refactor: replace context window heuristics with explicit model table * fix: account for context overhead in trim loop stop condition * fix: re-base context anchor when mode switch changes system prompt or tools * refactor: replace context estimation with usage-report-driven compaction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: chars/4 fallback for context usage when provider reports none Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: remove unused slide import failing svelte-check Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: re-seed context usage estimate on rewind so retry can compact Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: lazy read-side estimate fallback for context usage Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: fix reasoningRegistry mock to match resolveRequestReasoning Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
114c41251a |
feat(frontend): improve AI chat cancel and interrupted-turn handling (#9539)
* feat(frontend): improve AI chat cancel and interrupted-turn handling - Escape stops the in-flight generation when focus is on the chat (composer, messages, panel) — capture-phase listener so neither the session Monaco editor nor mounted-but-closed modals swallow the key - When a turn yields no output (or is cancelled before any), roll it back and restore the message to the composer - When a turn is cancelled or fails mid-way, keep the completed tool-paired steps and the partial answer text as context so a follow-up like "continue" picks up from there - Animate the thinking-block collapse like tool boxes (slide 150ms) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): address review findings on interrupted-turn handling - Guard the catch in sendRequest so a post-commit throw (e.g. saveChat) cannot commit the turn a second time or mis-flag the user message - Delete the persisted chat entry when rolling back a first turn empties the transcript (saveChat no-ops on empty, leaving a stale entry) - restoreInstructions skips when the user already typed a new draft - Use stopImmediatePropagation so one Escape on body focus cannot cancel several mounted chat panels at once Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(frontend): tighten comments and dedupe tests in AI chat changes Keep each invariant comment once at the place it would be broken; drop narration and repeated rationale. Remove near-duplicate test cases (chatLoop boundary permutations, cancel-before-output subset). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add comment policy to AGENTS.md core principles Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: comments must describe current code, not PR drafting history Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): treat completed reasoning-only turns as unsent A turn that finishes without abort but emits only reasoning produced a display bubble, so the empty-turn rollback (keyed on display output) skipped it and the user message was silently swallowed. Key the decision off usable output instead. Also trim comment blocks to the AGENTS.md 4-line norm, splitting rationale to its break-site. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
d3f5fe1c8c |
feat: enable native web search in copilot (#9522)
* feat: enable native web search in copilot * fix: add web search fallback and settings * test: use frontend uuid helper * fix: tighten web search fallback * fix: add web search error hint * fix: classify web search fallback errors * fix: avoid web search fallback tool error * fix: handle anthropic web search enablement errors |
||
|
|
5c20d6b4f7 |
feat: add global ai chat test tools (#9391)
* feat: add global ai chat test tools
* fix: avoid session id in flow test preview
* test: cover global flow preview ids
* test: require script and flow test tools
* fix: harden global flow test fallback
* Revert "fix: harden global flow test fallback"
This reverts commit
|
||
|
|
2f50e8bab0 |
feat(ai-chat): align footer bar + DropdownV2 mode/autonomy selectors (#9308)
* feat(ai-chat): align footer bar, use DropdownV2 for mode/autonomy selectors Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(dropdown): add `selected` item prop rendering a trailing check Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * style(ai-chat): add small spacing between chat input and footer bar Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ai-chat): always offer the 3 autonomy options in the auto-accept picker Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ai-chat): default autonomy mode to auto-accept on Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(ai-chat): use Button component for footer dropdown triggers Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * style(ai-chat): use a hand icon for the auto-accept-off autonomy state Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * style(ai-chat): use subtle Button variant for mode and model selectors Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * style(ai-chat): tighten spacing between input and footer bar Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ai-chat): reword autonomy levels as ask/auto-accept/bypass permissions Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(button): add 2xs unified size with tighter padding Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ai-chat): compact footer bar — 2xs buttons, AtSign context icon, short Yolo label, discreet model Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * style(ai-chat): widen the permission selector dropdown Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(dropdown): group shortcut + selected check to avoid ml-auto collision Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(ai-chat): cover getPersistedAutonomyMode default; clarify default comment Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ac26aa4e4c |
feat: add yolo mode for ai chat tools (#9258)
* feat: add yolo mode for ai chat tools * nit * fix: align chat footer controls * feat: add ai chat autonomy modes * feat: add autonomy mode dropdown * fix: highlight yolo autonomy icon * fix: auto accept flow edits * fix: hide unsupported autonomy modes * fix: handle auto-accept flow editor races |