mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-21 16:03:26 +00:00
5ea041b4c21b30ca8eaacbaffc7907a7cd681a44
30
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5ea041b4c2 |
fix: redact an adopted run against the schema its page ran
Redaction only visits the leaves the schema it is given declares, and the page runs the deployed version as it stands when Run is pressed. A deploy between this call reading the schema and that press can add a file argument ours never had, and its bytes then passed unredacted into the persisted card and the model's result, under the size limit that would have caught a large one. `adopt` hands over the schema it ran against alongside the job and arguments, and both redactions use it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ef870a52e4 |
fix: keep the refused form on the side it is showing
Closing the JSON editor when the form starts refusing writes mounted `SchemaForm` in its place, and `PasswordArgInput` mints a variable from the literal it opens on without consulting `disabled` — so a reader who typed a plaintext secret into JSON view had it stored by the act of entering plan mode, with no run and no Run press. The form no longer swaps sides while it refuses: `JsonInputs` takes `readOnly` and the editor stays mounted and uneditable. The JSON toggle is already hidden in that state, which is what keeps the fields from mounting from the other direction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
27cb8e7ae6 |
fix: refuse a run in the form, and show an adopted job's own arguments
Two findings on the deployed page carrying a chat call. `RunForm.run()` mints a variable per `password` argument before it calls `runAction`, so the plan-mode gate in the detail pages ran after the write it was there to prevent — and the keybinding reached it without the button. The refusal moves into the form, as `runRefusal`: set, it takes no writes at all, its submit buttons are disabled, and `run()` says so and returns before the secret processing. The page also stops offering the JSON editor under plan mode, instead of leaving one mounted that silently discards what is typed. An adopted job's arguments are the page's, and the card had no way to show them: it kept rendering the proposal, so a reader who edited a field saw the wrong values and the model was told nothing. `adopt` now hands them over with the job id, and they go through the same redaction and size guard as a run this tool starts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
88683188ce |
fix: carry a run proposal on the call, not in the tab url
The proposal reached the deployed page through the tab url's hash, which the
page already reads for "Run again". Two things were wrong with that carrier:
- A tab already open on the item does not remount when it is re-pointed, and
both detail pages read the hash once at init — so a second proposal for the
same item opened its form on the first one's arguments.
- Tab urls are persisted with the session, so a draft holding file bytes or an
un-minted `password` literal was written to IndexedDB. The transcript avoids
both deliberately, through `redactFileArgs` and `MAX_PERSISTED_ARGS_LENGTH`.
The call now carries its own proposal: `PendingRun.proposedArgs` reads the
card's live draft, the page seeds its form from it at init, and the tab's url
names only the call. `ItemViewerView`'s load key includes that name, so a tab
re-pointed to another call builds its form again.
Two more fixes on the same path:
- `adopt` was read off the prop after awaiting job creation, so a call that
settled during the request could be answered with another call's job. The
press now captures the call it was made under.
- An adopted job's arguments and version are the page's, which this tool cannot
read, but the model was told `Ran with arguments: {}`. It is now told the user
ran it on the item's own page, and that the arguments are the page's.
- Adopting hands the carrying tab to the run, so the page no longer navigates
there as well — that left two tabs open on the same job.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
f73fdde930 |
Merge remote-tracking branch 'origin/main' into glm/session-deployed-runs
# Conflicts: # frontend/src/lib/components/details/FlowDetail.svelte # frontend/src/lib/components/sessions/sessionPreviewTabs.svelte.ts # frontend/src/lib/components/sessions/sessionRuntime.svelte.ts |
||
|
|
0758bdc46b |
refactor: remove the frontend build-time base url (#11674)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
338c4e921d |
feat: operators get AI sessions (#11443)
* feat: operators get AI sessions `prefersSessionHandoff` stops taking the role, so every AI entry point an operator can reach — Ask AI, the home composer, Open in session, the flow editor's hand-off, the raw-app template picker — opens a session for them like anyone else. The sessions page drops its refusal, and the layout stops keeping them on the docked chat. In the sidebar they now get the Workspace/AI Sessions switch and session mode, and lose the Ask AI entry that stood in for them, since the pane behind it is no longer the one they are sent to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: operators get run-oriented examples in the home composer Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: note builder_flows in the operator composer examples comment Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: gate session tools and editors on operator rights Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: keep operator AI sessions behind a browser opt-in Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep flows refused in the prompt for operators without builder rights Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor: decide every frontend edit check through one editRights module Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: let admins who are also operators open session editors, as drafts.rs does Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: mirror drafts.rs exactly in roleCanDraft and correct the edit-rights docs Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
40ad39304e |
refactor(sessions): let the page run it and hand the call its job
The page kept the chat's arguments in step and ran on the chat's behalf, which meant reproducing everything `RunForm` does to arguments on the way through — the PowerShell split was the third thing found missing from that. It now runs the way it runs anything, with its own form, its own version and its own arguments, and tells the call which job that was. The tool follows that job instead of starting one. The proposal reaches the form through the url hash the deployed pages already read for "Run again", so nothing seeds it. `PendingRun` is three members, and `RunForm` goes back untouched but for the plan-mode gate. Two classes go with the copy. A version pinned on the page is simply the version that runs, since the page starts the job. PowerShell common parameters travel with it for the same reason. Plan mode still has to reach the page: a `password` mints as it is typed and the dynamic-select helper runs on mount, both before Run is pressed, and the page's own Run would otherwise start a job the chat promised not to make. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
530657b9eb |
docs(sessions): trim the comments on the adopted-run path
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
4ac2da5e98 | Merge remote-tracking branch 'origin/main' into glm/session-deployed-runs | ||
|
|
41162b270c |
feat: one page header across routes, and one sidebar for operators and developers (#11310)
* wip: one sidebar for operators and developers, with a detach mode Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: snap-detach the operator sidebar, and join the handle to the picker Dragging the sidebar into the left snap zone detaches it for an operator, who has no icon-only rail to collapse into; the width the drag started from is what gets persisted, so re-attaching restores the sidebar's size. The handle's colour pastille slides onto the card's workspace pastille as the card opens, alongside the workspace name, and both buttons that attach or detach the sidebar carry a tooltip. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: attach and detach from the sidebar header, open the card under its handle The button that swaps the sidebar between docked and detached moves to the header, next to the workspace name, so it stays in one place across the swap; the icon-only rail has no room for it and keeps the footer copy. The handle itself becomes a component of its own, carries the workspace icon its picker uses, and opens the card rather than docking — docking is the header button's job. The card is placed under the handle measured at open time instead of at a fixed offset. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: one page header across routes, with the workspace picker in it Every route now wears the same bar above its content: the sidebar toggle, the workspace picker, and whatever the route registers — an item's breadcrumb and summary, or a section name — with the route's own buttons at the far end. The bar's row is level with the sidebar's own top row, which the workspace ⇄ sessions switch now owns, so the two read as one band. A route fills the bar by mounting PageHeaderContent, which registers a getter rather than a snapshot: the layout calls it while rendering, so a path or summary the page edits reaches the bar without an effect keeping two copies in step. Nothing registers yet; the routes follow. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: detail pages fill the page header, which now spans above the sidebar A script or flow detail page no longer draws a bar of its own: its path becomes the header's breadcrumb, its summary keeps the rename-and-labels popover next to it, and its badges, menu and buttons ride along as the header's actions. The band runs the full width with the sidebar hanging below it. A snippet rendered in the header initialises its components under the header, not under the page that wrote it, so anything the page's tree provides is lost — the detail page's buttons read TriggerContext and threw. ContextBridge carries the entries a registration names back around the render. The floating handle is gone with it: the header's own toggle is the one control, hovering it peeks the sidebar in and clicking pins it. The workspace picker in the header is a denser variant of the sidebar's, and the card's outside-click catcher starts at the card rather than the viewport top, so it no longer swallows clicks meant for the toggle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: one breadcrumb in the page header, and the banners that used to sit under it The header band now carries a single breadcrumb — workspace, fork, then the item's path — built from one component, so every segment has the same size, weight and icon size, and slashes separate them. The item's kind icon rides with its summary, after a dot, rather than with the path. The fork and drafts banners move into the band as badges plus a subtle Review button: ahead/behind counts instead of a bare change total, and the per-kind breakdown stays on the compare page. The home hero's CLI, MCP and Hub links move into an ellipsis menu, alongside the toggle that pins the session chat to the page. The flow editor and the app viewer hand their bar to the band too, and a route can drop the band's bottom border when its own content draws one — the border stays in the box with a transparent colour, so contents never shift a pixel across a navigation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: the runs page's own header row moves into the page header The title, the queue counters, the kind and status filters, the schedule and future-job switches, the search field and the timeframe picker all ride the band now, and the page starts at the chart — one row of vertical space back. The two toggle groups appear from 1500px: below that the bar cannot hold them next to the search field, and the same filtering is reachable from the search field's own kind and status chips. The search field is what gives width back, and the breadcrumb yields last, so a page's own name stays whole while the workspace and fork names truncate. The queue counters keep their icon form at every width, since the bar has room for two counters and never for the two labelled blocks. Their counts hang outside their icons, so the group carries the margins that keep a badge off its neighbour, and the suspended counter answers a hover at zero rather than sitting there as a dead icon. The band is 44px rather than 40, and a page can hand the breadcrumb a hint — the documentation tooltip that belongs with the page's name rather than with its buttons. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: no border under the page header, and the peek card closes on the way out The band draws no bottom edge and no page asks it to, so `barBorder` goes with it — a page that needs the separation draws its own line, as the detail pages now do above the run form and the tab strip. With nothing toggling a border, the band is a flat 44px on every route. The detached sidebar's card is a hover affordance, so it leaves when the pointer does: off the handle and off the card both, on the same debounce a hover menu uses, because the two do not touch and the leave of one fires before the enter of the other. The mobile drawer is opened deliberately and still stays until it is dismissed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: one band, one workspace part, and the pickers that hang off it The band is a single element whose placement is styled rather than branched: a page registering a side-panel inset used to swap it between two `{#if}` arms, destroying the header and rebuilding it a frame later — a blink of no header with the content sliding up into the gap. The workspace and its fork become one breadcrumb part: the name leads home (inert in a session, where the workspace is the session's scope rather than a place to go) and one chevron opens the picker, which already expands a family to reach its forks. The picker's panel hangs from the part's left edge, opens with the active family expanded, and closes before the switch runs — the switch renames the workspace, which moves the trigger the panel hangs from, and floating-ui was chasing it across the viewport. The item's path is a plain label rather than a row of pickers: same separators and weight, and a click copies it with its own confirmation. The kind icon goes, the page below says what kind it is. Around them: a "prod" badge for a root workspace, the session's acting fork beside the session's name rather than in the breadcrumb, the edit-in-dev affordance as a button in the header, the drafts count as a sentence, and a fade when the workspace changes under a page that stays mounted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep a page's own controls when the workspace navigation is hidden `menuHidden` means "this is an embed, don't show the workspace's navigation" — but Runs, the detail pages and the flow editor now keep their controls in the band and nowhere else, so hiding it took the filters, the timeframe and the page's buttons with it. A session's preview iframe opens exactly that way. The band now renders for an embed too, without its breadcrumb, so the page keeps its controls and the embed still gains no way to navigate the host's workspace. The flow editor's autosave indicator moves into the header's actions with the rest of its bar: saving state, the autosave toggle and other people's drafts were left behind in the branch the full-page editor no longer takes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: the header carries what it promised, and nothing it does not The registry's item described pickers the breadcrumb no longer has: nine fields — the editability flags, the change callbacks, the navigation hook — that nothing read, while the flow editor passed eight of them and quietly lost its inline rename. The type now says what the bar does: a path, and either a summary or the page's own control for one. The flow editor renames through that control, as the detail page already did. `barPlacement` went the same way: no page registered 'inline', so the layout's guard never fired. The sessions page registered an inset of 0 whenever its preview pane never mounted — the "no session selected" and opted-out arms — and 0 reads as "a page owns the right edge", floating the band over the page's first line. It registers an inset only once the pane has a width. The band's sidebar handle docks through `setDetached` like the card's own toggle, so docking still suppresses the rail's entry animation and closes the card; a summary without its breadcrumb no longer leads with a stray separator in an embed; a page's hint sits with its name whichever way the page named itself; and `Menu.closeInstantly` and `EditorHeader`'s `inline`/`hideSummary` go, their callers having been replaced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: a deployed app gets the viewport, and the header waits in the corner An app is the page, not a page inside Windmill's chrome, so the band leaves the flow and floats above the viewport until the handle in the app's corner calls it down. The handle is faded until the pointer nears it, hovering peeks the band over the app, and a click pins it — one flag for every deployed app, in this browser, because someone who wants the breadcrumb over a dashboard wants it over the next one too. Peeking overlays rather than pushes: the app is an iframe, and taking 44px off its box on every pass of the pointer would re-lay-out whatever it draws. Pinned, the band is in flow like any other route's and the app starts under it. The sidebar is untouched — docked stays docked, detached stays a peek — since the pin is about the header alone. A page says it wants the viewport with one registry field, so the layout keeps asking the registry rather than knowing about apps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: one corner control for the chrome an app page hides Hovering the button overlaid on a deployed app brings back whatever that page is hiding: the band always, and the sidebar card too when the sidebar is detached. The card hangs from this button rather than from the header's own handle, which is off-screen with the band. The handle in the band no longer attaches the sidebar — it reveals it and nothing more. A control that hides the thing it sits on cannot also be the way to bring it back, so attaching stays with the toggle in the sidebar's own footer, where detaching happens. The band's two buttons go in the order the things nest: the sidebar's handle, then the header's pin. The overlaid button stands where the pin will be once pinned — same 28px box, and a slot further along when the sidebar's handle takes the first — so pinning moves nothing. It wears the panel pair the sidebar's toggle uses: solid while the band is away, dashed while it is shown. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: the access point sits in the corner, and the header covers it The button is in the page's top-left corner — 8px in, or just past the rail when the sidebar is docked — rather than shifted along to where the header's pin would land. Hovering brings the header down over it: an access point that the regular view covers once it arrives, so there is nothing to line up and nothing to move. Because the header lands on the button, the pointer leaves it the instant it arrives. The band holds both itself and the sidebar card open while the pointer is anywhere on it, or the card would retract under a header that stayed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: on an app page the floating button owns both, and they move together The band drops its sidebar handle there: the floating control in the corner reveals the sidebar along with the band, and two handles for one gesture is one too many. The two are then linked — the pointer resting on either holds both, and leaving either collapses both — so they behave as the single thing the one button revealed. The card meets the header in a line: it hangs from the header's bottom edge rather than from whichever control opened it, which had it starting a couple of pixels high. With no header to sit under — an embed — it still hangs from the control that reached for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: the revealed sidebar is a panel under the header, not a card over the page No rounded corners, no inset from the window: it starts at the header's bottom edge and runs to the left and bottom edges, with one border down its right side and a shadow to lift it off the page. A card floating in from nowhere read as a separate thing; this reads as the sidebar the header sits on top of. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: only an app page gets the flush panel; elsewhere the card still floats Over an app the sidebar reads as the panel the header sits on — flush left and bottom, square, one border down its right side. On every other page it is what it was: a card inset from the window with rounded corners and a border all round. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: an app opens with the header already away Opening an app showed the header and then slid it out: the page claimed the viewport on mount, and the band animated from where it had been rendering. The slide belongs to the hover, not to arriving, so it is armed a frame after the claim — the first hide is a cut. The claim moves to the route, which mounts before the viewer waiting on the workspace to resolve. A cold load now paints the band away from the first frame instead of showing a header that leaves again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: an app's Edit sits with its name At the far end of the bar it was a journey across the window from the thing it edits — on a page whose header is only there while the pointer holds it, a journey with a time limit. It goes beside the app's path instead. The slot it uses is the one Runs' documentation tooltip already had, renamed from `hint` to `afterName` and documented for both: what belongs to the page's name, whether that is a mark or a control that acts on what the name points at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: developers collapse, operators detach One control each, rather than three for everyone. A developer switches the sidebar between icon-only and full width and never detaches it; an operator, who has no icon-only rail to collapse to, hides it instead — by the button in its footer or by the same drag that collapses it for a developer. The way back survives either way: a detached sidebar keeps its "Show sidebar" button for anyone, so a developer who detached before this was so is not left with a sidebar they cannot return. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: a page is as tall as it is given, not as tall as the screen The band takes 44px off the top of every route, so a page sized to the viewport overflows by exactly that much: h-screen inside a column that already lost the band's height. These pages measure the box they are in instead. CenteredModal gains `inPage` for the five pages that render it inside the layout rather than over it — the same modal, centred in what is left of the page rather than in the window. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: the band narrows, wraps, and draws its own edge The band publishes its width, so the pages filling it can answer it. Two tiers: under 1000px the breadcrumb keeps the workspace disc and its env badge and drops the name, and under PHONE_BAR (800) the trail stops yielding at 10rem — that floor is what makes the bar wrap, so a page's buttons take the line below rather than squeezing the name to an ellipsis. The band also draws the line between itself and the page, and only while something is scrolled under it. One capture listener on the window sees every scroller, since most pages scroll inside their own box and never move the layout's. An inset shadow, not a border: a border would move every page down a pixel the moment the line appeared. DetailPageLayout and InWorkspaceAppViewer drew that first line themselves; two lines would stack, so theirs go. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: the script, app, runs and audit pages fill the band Each of these kept a header of its own above the page. They register with the band instead: the breadcrumb names what the page is on, and the page's buttons sit at the far end of the one row. They answer the band's width rather than the window's, since the buttons share that row with the breadcrumb. The script editor folds Diff and Settings into the menu it already shows when compact, the app editor drops the panel buttons, Awareness and Export and goes icon-only on Deploy and the mode switch, the flow editor menus its diff, and runs hides its counters. Runs also declares `actionsFlexible`: its search is the one control here that can give width back, and a box that refused to shrink would make it take its maximum instead. The run detail page loses its ActionRow, and the audit log its big title — the breadcrumb says where you are, and the page keeps its tooltip. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: one sidebar column, shown as a rail or as a panel The rail and the drawer held two copies of the same column — the same sections, in the same order, in two pieces of markup, so a change to one silently skipped the other. Both now render `sidebarColumn`, and the shells keep only what differs: the rail its width, resize handle and collapse button, the panel its outside-click catcher and its transform. A phone gets that panel rather than a modal. There was a width test deciding between two presentations; the card under the band's handle works at any width, so it goes. What is left of the width test is the choice between a rail and a panel, plus a `(hover: hover)` test — the old `innerWidth >= 768` was standing in for "has a pointer", which is what decides whether the panel closes when the pointer leaves it. With the card everywhere, the backdrop, the `aria-modal` (nothing dims and nothing traps focus) and the drawer's close cross all go: the band's handle opens the panel and a tap outside closes it. The burger row goes with them. Entering panel width now collapses the rail transiently, so coming back to a wide window does not land on a rail the page was resized away from. The band also stops easing its right edge. That transition belongs to the rail, which moves the band's left; its right follows a splitter the page owns, and easing it left the band lying over a session's side panel — a blank strip across that panel's own header for 200ms after every drag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: the home composer flies to the menu that restores it Removing the composer puts it away rather than throws it out, and the way back is one item in the band's menu — easy to miss on a page the composer has just vacated. A mark flies from the control that removed it to that menu, and the menu answers with one ring as the mark dissolves, so where it went is witnessed rather than explained. The cross and the menu item now read "Remove session chat", one name for one thing whichever a reader reaches for. Reduced motion skips both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * revert: keep operators out of AI sessions That change is its own PR; this one is about the navigation. `prefersSessionHandoff` takes the role again and every entry point passes it, the sessions page refuses an operator arriving by URL, and the layout leaves them on the docked chat. Two clauses are new rather than restored, because the nav rework is what gives an operator a sidebar at all: the Workspace/AI Sessions switch and session mode are both hidden from them, since the page behind them declines. `askAiShown` gains the operator case for the same reason — they keep the docked pane whatever the beta gate says, so the sidebar has to offer a way into it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep every control the band took over reachable Four controls the page-header conversion could put out of reach, each found by a review pass and reproduced in the browser: - The flow editor's preview buttons. `topbarWidth` is bound only in the branch a full-page editor does not render, so `compactTopbar` was permanently false there; below the 800px phone bar `phoneTopbar` dropped the buttons inline and the graph overlay never took them, leaving FlowPreviewButtons mounted nowhere and step testing, whole-flow testing and "Test flow & record" no-ops through optional calls. It now measures the header the way ScriptBuilder and AppEditorHeader do, and the inline and overlay conditions are exact complements. - The pin on a deployed app. The band lands over the corner handle that called it down, so the pointer that arrives there hits the band. The band carries the pin itself now, on the same pixels, in place of the sidebar handle it hides. - The detached sidebar's own menus. They portal to `body`, so moving into one left the card and closed it 200ms later, sliding it out from under the menu being read. The card holds while the pointer is in a menu and takes the close from the leave of that. - A deployed app's name for a read-only viewer. The breadcrumb was registered behind write access, so without it the trail fell back to the section name "Apps"; and the Edit button rode `afterName`, which the band drops for an embed along with the trail. The name is registered for whoever opened the app, and what sits after it survives `navHidden` as the page's own control. navPageFor gets a table test: it answers both the sidebar links and the breadcrumb label, and its longest-prefix and first-segment rules are what made the "Apps" fallback look like a correct answer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: drop what the operator revert and this refactor left behind The operator tour's last step named AI sessions among the pages the sidebar opens, which operators have no way to reach. The nav handle's comment named the AI chat layout's menu button as a second way into the card; that button and its prop are gone, and the corner control on a page that hides the band is what stands in for the handle now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * style: prettier the three files this branch left unformatted `prettier --check` fails on all three at the previous head. Whitespace only, apart from one Button tag that fits on a line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: give a page without the band's chrome what the band was carrying Second review round, three reachable defects plus one decision: - A public raw app was 150px tall. The bundle's iframe is `h-full`, and dropping its `min-h-screen` left nothing to resolve against on the share, embed and custom-path routes — none of which has a layout above them. Measured: 150px in a 900px viewport. The floor goes back for those routes and stays off inside the workspace, where the layout hands a height down and `min-h-screen` would overflow it by the band. - The band retracted out from under its own open menu on a deployed app. Last round taught the sidebar card to hold while the pointer is in a portaled menu and left its sibling alone, but the band opens menus too — the workspace disc, the fork family, every path level. One helper now, used by both closers. - `bandPeeked` outlived the page it belonged to. Clicking a control in the band navigates away, and the next page may not be one the band floats over, so the hover handlers that would clear it are gone — leaving the band already down when a full-bleed page came back. `beforeNavigate` clears it. - An embed has no band to carry a control, so the app's Edit floats over the canvas there, where it sat before the band existed. Registering it as the band's `afterName` under `navHidden` instead would have cost the app 44px of the iframe for a row holding one button. Reading "is the workspace navigation hidden" now has one definition (`isMenuHidden`), since a page has to answer it too: the flag is sticky inside an iframe, so the raw query param is the wrong thing to read. Also inset the band by the rail only when there is a rail — an embed reserved its width and crowded its own controls into a compact layout early. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: a path that names nothing yet has no breadcrumb leaf `splitItemPath` documents that it gives up on a path still being typed, but `f/demo/` is three parts and returned a leaf with an empty name — a blank segment in the trail. The editor binds the path live, so the breadcrumb sees every keystroke. Tested here rather than `navPageFor`'s sort: no two `NAV_PAGES` paths nest, so the longest-prefix case that test claimed to pin has only one candidate. Renamed it to what it actually checks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * style: prettier the four remaining files this branch left unformatted All four fail `prettier --check` on the branch and pass at the merge base. `raw_apps/utils.ts` also fails but already did at the base, so it is left alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: the raw app editor fills the band like the other three It was the one editor route still rendering its own bar under the band, so it showed two header rows and the band named the app "Apps raw" — `navPageFor`'s first-segment fallback, because nothing on the route registered an item. Its top bar now becomes the page header the way the script, flow and low-code app editors' do: the path and summary are the breadcrumb's, the file-sidebar toggle, autosave cloud, presence mark, menu, Diff, Jobs, export, AI and Deploy are the header's actions, and the path is a plain label since this editor renames from its deploy drawer. The actions render under the band, outside the subtree `RawAppEditor` declares the operating workspace in, and `OpenInSessionButton` asks that context who the acting user is — so the workspace travels with them. `operatingWorkspaceContextEntry()` hands it over without exporting the key. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the folders a half-typed path already names The trailing-separator guard gave up on `f/demo/` entirely, which dropped the whole trail to the kind chip and brought `f/demo` back on the first character of the name. The levels the path does name come back now; only the leaf waits. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: pin holds the first slot, and the nav table says what it shares Pinning brings the sidebar's handle back, and from behind it the unpin button sat one slot over from where the pin had just been clicked — so a second click in the same place opened the sidebar instead. An operator is detached by default, which makes that the default state of the feature rather than an edge case. `NAV_PAGES` claimed one table stops a page being called one thing in the nav and another in the header, which holds for the seven entries the sidebar's main group is built from and not for the four grouped separately — `/routes` was "Triggers" in the header and "HTTP" in the nav. The comment now says which is which, and that entry is named for the page rather than its group. `sidebarPageAllowed` gets a table test: it decides what an operator sees, and its `=== true` is what a future `Option`-style default would silently change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: the band names the path Deploy will create, and keeps the summary readable A brand-new app is parked at a `draft_<uuid>` placeholder, and the band named that rather than the path its Deploy will create — `newEditedPath` refuses the placeholder for exactly this reason, and the editor's own bar read it. Both app editors register it now. Verified: the trail reads `u/admin/gentle_app` while the URL is still the draft. The band's summary gets the `title` the editor's own bar had, so one the bar truncates can still be read on hover. The wrapper that hid AppExportButton on a phone never hid anything: the component draws no trigger — it is the export drawer, which portals to `body` — so the class toggle was inert in both states and the comment read as if something moved. Dropped from both app editors. Two comments corrected: `NAV_PAGES`' `icon` does reach the breadcrumb for all eleven entries (`NavBreadcrumb` renders `routePage.icon`), so only `operatorKey` goes unread for the four the sidebar groups separately; and the raw editor's phone note described folding that does not happen — the file-sidebar toggle stays in the band, Jobs and the export are already in the menu, and AI is what stands down. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: an item the picker files under no kind keeps its name in the band The merge brought main's agents detail page, which reaches DetailPageHeader with no `errorHandlerKind` — and the band's item required one. `WorkspaceItemKindIcon` falls through to the script glyph for anything it does not know, so defaulting would have labelled an agent a script; the kind is optional now and the trail names the item without claiming it is one of the three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: the file sidebar's toggle leaves the band The band is the workspace's header, not the raw editor's layout controls. ⌘B still folds the file sidebar, and the editor's own bar keeps the button where it draws one — a session pane. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: the scroll edge stops measuring, and the comments describe the code The band's edge listener captures every scroller in the app — Monaco, the audit table, a run's logs — about once a frame while any of them moves, and it took two `getBoundingClientRect()` reads before looking at `scrollTop`. It now answers from `scrollTop` first and only measures when that answer differs from what the band draws, which is the transition rather than the steady state. The rect check still authorizes every change, so a scroller that does not start at the content's top still cannot claim the edge. The two agent routes main just added size themselves to the viewport, which is 44px more than the box the band leaves them. They measure 856 either way — the layout's `min-h-0` column clamps them — so this is the sweep the rest of the branch already had, making the height follow the box rather than depend on a parent's clamp. Six comments described the conversion rather than the code, which AGENTS.md calls out by name. Each now states the constraint a reader could break: why the summary keeps its popover here, why the audit hint is `afterName` and not an action, why a run registers a `section` rather than an item, why the Runs filters are `actionsFlexible`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Revert "fix: the file sidebar's toggle leaves the band" This reverts commit |
||
|
|
463c2aa9a5 |
fix(sessions): keep the submitted arguments on screen after Run
Pressing Run settles the call, so the draft the form reads stops answering before the job exists: the form emptied for as long as the start took, and stayed empty when the server refused it — leaving the reader the run to make again with nothing to make it from. It falls back to what it submitted. The note on the PowerShell split also claimed the unoffered keys travel to the job. `SchemaForm` holds the arguments to the keys the schema declares, so they do not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
9577c913a7 |
refactor(sessions): let the page edit the call's own draft
The page kept a copy of the arguments and handed it back when it went away, so
everything `RunForm` does to arguments on the way through had to be mirrored on
the way out: the PowerShell split was the third thing found missing from that
mirror. The form now binds the draft the card binds, so there is one set of
arguments and nothing to carry anywhere.
That removes the hand-back, the destroy hook holding it, the `initialArgs` the
form was seeded through and the guard that stopped an unseeded form wiping the
proposal. A call arriving on an open tab still rebuilds the form, which is what
`{#key}` is for; it now opens on the draft rather than on a prop.
The PowerShell section follows the rule the rest of the run settings already
follow: a call carries arguments and nothing else, so the section is not offered
while the form stands in for a card — nothing can be chosen there and lost. Its
keys stay in the arguments when the section is not offered, so a proposal
carrying them reaches the job either way.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
863dbb61e3 |
fix(sessions): hand back the PowerShell common parameters too
`RunForm` keeps `_wm_ps_*` out of `args` and puts them back only as it submits, so handing `args` to the card dropped whatever the reader had chosen on the page — ErrorAction among them, which falls back to Stop. The form now answers with what a run would send. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b824fe3ffe |
fix(sessions): hand back only what a form actually held
A tab closed while the deployed item was still being fetched handed the card an empty object, wiping the proposal it was still parked on: the page registers its destroy hook long before `RunForm` gives `args` a value. The comment above it also claimed every way out destroys the page. The next call re-pointing the same tab does not, and that page's edits stop there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
8fb8d2a329 |
refactor(sessions): seed the run form as it is built, not after it mounts
The form takes what it opens holding as a prop, and the page renders it under the call's id. A request arriving on a tab that is already open builds a new form already filled, so there is no mounted instance to reach for and nothing to wait on — which is all the seeding effect was doing. That also settles where the hand-back reads its call: with no instance to track, `onDestroy` reads the prop, which is still there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5369802585 |
refactor(sessions): hand the form back on destroy rather than from an effect
Every way the page stops carrying a call and still holds edits destroys the component: the tab closes, or its url changes and `loadKey` remounts it. So the hand-back is a destroy hook, not a reaction to the call changing — which needed a key and two `untrack`s to keep its own write from re-running it. The call is the one this form was seeded for, captured where it is seeded. By the time the hook runs the tab has already stopped naming it, so reading it then would find nothing to hand back to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
1a2821d711 |
fix(sessions): close the JSON route into a read-only run form
Disabling the fields left the other way in: arguments written as JSON reach the same object, and a password set there is a plain literal until the schema field mounts onto it — which mints it whether or not that field is disabled. The editor's writes are refused while the form is read-only, and the toggle offering it is not drawn. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2fe3579f75 |
fix(sessions): take no writes on a relocated form under plan mode
A password field mints an ephemeral workspace variable as it is typed, so withholding the dynamic-select helper was half the promise: the field itself has to be unwritable. One flag now covers both, as the card's own form already did. The hand-back is keyed on the call rather than on the object naming it, which is rebuilt whenever the draft or the posture changes — its own write to that draft is what would have re-run it. It also hands back only what its own form held: the next call for this item re-points the same tab without remounting, and its seeding can land first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
88457fbe16 |
fix(sessions): keep the page's promises and its edits when it holds a call
The dynamic-select helper runs its job when the field mounts, not when Run is pressed, so a form relocated to the page ran one under plan mode — which the card withholds for exactly that reason. The page owes the same promise while it is standing in for a card. Its form is a copy, so arguments typed there were dropped when the call went back to the card: it then offered, and ran, what the page had opened with. The call now carries them back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d5b96105c6 |
refactor(sessions): let the tab carry the call, and leave chat flows alone
The page that answers a parked run form was found by item, so anything on that page could resolve the call and act on it: a version picked after the fact, a press that outlived its call, a send in some other conversation. Each was fixed where it surfaced, and the next one surfaced somewhere else. The tab carries the call now, named in its url beside the version pin. Only the tab opened for a call can answer it, and re-pointing that tab anywhere drops the name, so a version picked on the page hands the call back with nothing to check for it. The item-keyed map and the lifecycle hooks around it are gone. A chat-enabled flow's page is a conversation with no run form, so it cannot answer a call at all: Preview opens it to look at and the card keeps the run, which is what the agent needs to report a result. That deletes the composer seeding, the strip above it, and the "Handed off" outcome that existed only to describe a run the agent never saw. Prefilling that composer is worth having on its own and comes back in its own PR. Running a deployed chat-enabled flow needs a memory id, which this path never sent: the card's Run answered with a 400. Each such run now starts its own conversation on the flow's page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b9cd05d2bf |
Merge remote-tracking branch 'origin/main' into glm/session-deployed-runs
# Conflicts: # frontend/src/lib/components/sessions/ItemViewerView.svelte |
||
|
|
8c74f75de3 |
fix: simplify the run page AI card to a prompt and a chat button (#11561)
* fix: drop the add a prompt for AI button from the run page * fix: open the run page in the session preview from a plain chat button * fix: rename additional prompt for AI to AI prompt * fix: put the run page chat button beside advanced when no AI prompt |
||
|
|
7eff62ce17 |
fix(sessions): bind the page's async work to the call it started with
The page reads the call it is carrying from a derived, and every await on it outlives that read: a call can settle and the next request for the same item be adopted inside one `processSecretArgs` round trip. Read live on the far side, the page would submit one request's arguments to another, decline a proposal the reader had not been shown, or seed a composer over the one they were looking at. Each of these now holds the call it began with. The form's own press holds its guard too, so a replacement cannot have its latch released by the press before it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0ac9b56ba3 |
fix(sessions): hand the call back when the page cannot honour it
A page carries an adopted call only while it can run what the call asked for. Picking a version broke that silently: the tool starts its job by path, so Run on a page pinned to an older version ran the deployed one instead — the page said v1 and the job returned v3. The page now hands the call back when it is on a version, and the card takes its form back, which is where the call can still be confirmed as proposed. Whether the call is still waiting is the manager's answer, not the adoption map's. Reading it live covers the ends that reach no hook: a submit whose job never started, because the server refused it or plan mode blocked it after the arguments were handed over. Those left the page with a dead Run and no way out but the X. The page's Run also has to clear what the card's clears before the form mints a password into a workspace variable — plan mode, and a second press. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
72ea7d416a |
feat(sessions): run a deployed item from its own page, for the chat
The run card stays in the chat by default. Preview opens the item's deployed page in the preview panel with the proposed arguments already filled, and Run there hands them back to the parked tool call — so the chat still owns the job and reports on its result. A chat flow has no run form to fill: its page is a conversation, so the proposed message goes into the composer and a strip above it says what was asked for. Sending it runs the flow in that conversation, which settles the call with a reason rather than a refusal — the card says "Handed off", not "Cancelled". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e952298f84 |
feat: replace the AI input filling toggle with an additional prompt (#11501)
* feat: replace the AI input filling toggle with an additional prompt for AI * feat: pass the additional prompt for AI to MCP clients and shared AI guidance * feat: shrink the run page AI card to a button and a collapsed prompt * fix: offer writers a way to add a prompt for AI from the run page |
||
|
|
904abda69b |
fix(sessions): apply a run proposal once, not on every remount
The detail page remounts under its viewer whenever the item is deployed, refreshed or pinned to a version, and came back with its own latch reset — so the agent's proposal was pushed in again, over whatever the reader had typed since, bringing back a note they had dismissed. The proposal was made once. The viewer now holds it as pending until the page reports applying it, and says separately whether to credit the agent: the note stands until dismissed, the values land once. Also from the CI round's nits: - a pinned viewer the handover re-points is brought to the front, since the call is about to say the page is open - offerMessage's doc described inputs it no longer carries; applyInputs owns them, deliberately unconditional on the message landing Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
8f33e7877e |
feat(sessions): hand a deployed run to the item's own page
A deployed `run_script` / `run_flow` opened a form in the chat and started the job itself. It now opens the item's deployed page in the preview panel, fills the run form that is already there with the arguments it proposed, and the call ends. The run is the reader's: they start it from the page they would have run it from anyway. **Why the tool stops owning the run.** What the tool could carry was arguments and nothing else, so the page had to hide the controls it could not forward — scheduling, tag override, invisible-to-owner — and "Schedule to run later" ran immediately. It also ran HEAD whatever version the page was pinned to. Handing the page over costs the model nothing it had: it never ran anything, it waited for a reader to press Run. Now it says so and moves on; `get_run` is there when it needs the outcome. A TEST run is unchanged. It executes the draft being written, which the deployed page does not show, so it keeps the chat's own form. So does the bypass posture, which answers for a reader who has already consented and would leave nobody to press Run. Plan mode is unaffected: its gate already sits before the form, so it refuses before anything opens. **On the page.** The arguments arrive narrowed against the deployed schema by the tool that fetched it, and a note above the form says where they came from; dismissing it drops the note and keeps the values. A chat flow has no run form — its page is a conversation — so the proposed `user_message` goes into the composer instead, for the reader to edit and send. The composer declines if they were already typing. **On the card.** The call settles as `Opened`, with the arguments it filled under Inputs and no outcome pane: nothing ran, and a Result tab could only say the run came back empty. The eye reopens the page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b10f83c763 |
feat(sessions): show an item's deployed page in the preview panel (#11458)
* feat(sessions): show an item's deployed page in the preview panel The preview panel showed an item's editor and nothing else. It now shows the deployed page too, as a tab of its own. **Two sides, two tabs.** The editor and the deployed page are different things to look at, so `isItemTabFor` keys on the side rather than the item: asking for a side already open focuses it, asking for the other opens it alongside. Two editors for one item still cannot coexist — they would race its single (kind, path) cell — but an editor beside a viewer is safe, since the viewer reads the deployed version over the API and holds no cell. The tab breadcrumb keeps re-pointing in place; `Exit & see details` and the detail page's `Edit` open the other side instead of consuming the one you are on. A tab's label carries `(edit)` when it is the editor side. **The detail pages moved out of their routes.** `/scripts/get` and `/flows/get` are now thin wrappers over `ScriptDetail` and `FlowDetail`, which the panel renders too. Everything they reach — drawers, triggers, saved inputs — is scoped to the viewer's workspace through `setOperatingWorkspace`, and every navigation they attempt is caught by `interceptNav` and turned into a move inside the panel, so nothing takes the browser out of the session. A plain click is intercepted; ⌘-click and middle-click still open a real tab, which is why the pages keep their `href`s. **The picker opens what exists.** A row opens the deployed page, or the editor when nothing is deployed there, and carries a Draft / Draft only badge in the review dock's words. `WorkspaceItem` gained `draftOnly` and `hasDraft` from the listers, which already returned both; deploys now invalidate the picker cache through `itemDeployed`, so a just-deployed item stops reading as a draft. Rows also carry a hover Edit action, reachable from the keyboard with the pick modifier. `open_preview` gained a `mode`, narrowed out of the advertised schema for a session that cannot write drafts and re-checked per path in the handler, where a refusal reports "couldn't check" apart from "denied". The mode is resolved before those checks, so a call that omits it is gated as the editor it will become. Alongside, the workspace a detail page acts on is now the one it is showing rather than the one the browser is navigated to: `MoveDrawer`, `toggleWorkspaceErrorHandler` and the pages' own permission gates read the operating workspace and its acting user, and `RunForm` mints a password argument's ephemeral variable there too — in a fork session all of these previously answered for the parent. `InWorkspaceAppViewer` hands the app's `ctx` user down as a prop instead of writing the global `userStore`, which from a session tab was re-pointing every permission check on the page. Fixes found on the way: `DetailPageLayout` claimed `h-screen` inside a panel that is not the viewport; Edit on a historical script version dropped the version from the intercepted click; the Run button stayed spinning after a run that left the page mounted; and the window-level run shortcut fired from a collapsed panel and from keys another handler had already claimed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(sessions): historical edits, stale not-found, shared edit rights, tab labels Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): keep deployed-page links and workspace reads in the session Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): scope detail hrefs to the session, re-point the viewer's own tab Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): keep a previewed raw app's route out of the session URL Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): resolve edit-in-fork against the session workspace Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): read advanced run tags from the session workspace Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): fork from the session workspace, star only navigation items Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): mount only the side a preview tab shows Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): address CI review round 1 findings Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): route links in a deployed page's drawers through the panel Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): tell the chat which deployed page the panel shows Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): only a 404 reads as an undeployed raw app Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): scope the unparseable-JSON run gate to the form Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): a flow deleted from the panel stays in its tab Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): the picker's edit shortcut works on the current row Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): load-error toasts say what failed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(sessions): keep an oversized invalid JSON editor in its form's run gate Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * revert: keep main's draft_only description in openapi.yaml Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |