mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-21 16:03:26 +00:00
* feat: render an AI agent result as its answer, not as raw JSON Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: sanitize agent markdown through the shared plugin chain Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: fold agent stream events incrementally per poll Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: separate the agent meta line from the result toggle group Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: add a transcript view of an agent run's conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: retire AIAgentLogViewer in favour of the transcript Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: show the partial transcript a max-iterations failure carries Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: move the agent meta line and system prompt below the conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: show an agent run as what it did, not as a conversation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: name the agent run breakdown a trace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): label the save-as-agent form fields per the guidelines Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): reuse the resource form's path and description fields Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): keep the action tags on a max-iterations failure Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): keep offline replay inert and the streamed answer to one turn Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ai-agent): reset the streamed answer on providers that skip tool_call Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: carry the event type narrowing through the stream parser Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: survive a malformed message rather than take the viewer down Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: coerce agent messages once at the parse boundary Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: show an agent run as one scroll ending in its output Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep every streamed turn instead of dropping the narration Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: share the chat divider and drop the unsafe run auto-scroll Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: give the labelled divider a border colour and the standard pretty icon Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: pad the agent run below its badges as well as above Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: follow a streaming run's pane without moving the page Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: share the chat's stick-to-bottom mechanics with the agent run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the answer's citations and end a turn's reasoning with the turn Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: read the agent stream through the windmill-chat sdk parser Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: show an agent run's thinking instead of falling back to raw json Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop a stream the fold cannot use from claiming the run pane Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: identify an agent run by more than the job id the replay withholds Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: drop the tests and comment lines that were not earning their place Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop a streamed turn's text shifting when a tool call closes it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
48 lines
1.5 KiB
TypeScript
48 lines
1.5 KiB
TypeScript
/**
|
|
* Keeps a growing pane pinned to its end, for the chat transcript and the agent run
|
|
* viewer. Shared for one non-obvious guard: a programmatic scroll dispatches its
|
|
* `scroll` event asynchronously, so content landing in between widens the gap for a
|
|
* tick, which reads as the reader scrolling away and disengages the follow.
|
|
*/
|
|
|
|
/**
|
|
* Distance from the end within which a reader counts as still following. Allows
|
|
* for sub-pixel rounding from `scrollTo` and the occasional overscroll bounce.
|
|
*/
|
|
const STICK_TO_BOTTOM_PX = 8
|
|
|
|
/** A scroll event this close after our own scroll is ours, not the reader's. */
|
|
const OWN_SCROLL_WINDOW_MS = 120
|
|
|
|
export type BottomSticker = {
|
|
/** Jump to the end. Instant: smooth would animate every append and race the next. */
|
|
scrollToEnd: (pane: HTMLElement | undefined | null) => void
|
|
/** Whether the pane is at its end, i.e. the reader wants to be carried along. */
|
|
isAtEnd: (pane: HTMLElement | undefined | null) => boolean
|
|
/** Whether the scroll event being handled was one we caused. */
|
|
isOwnScroll: () => boolean
|
|
}
|
|
|
|
export function createBottomSticker(): BottomSticker {
|
|
let scrolledAt: number | undefined
|
|
|
|
return {
|
|
scrollToEnd(pane) {
|
|
if (!pane) {
|
|
return
|
|
}
|
|
scrolledAt = Date.now()
|
|
pane.scrollTo({ top: pane.scrollHeight, behavior: 'auto' })
|
|
},
|
|
isAtEnd(pane) {
|
|
if (!pane) {
|
|
return false
|
|
}
|
|
return pane.scrollHeight - pane.scrollTop - pane.clientHeight <= STICK_TO_BOTTOM_PX
|
|
},
|
|
isOwnScroll() {
|
|
return scrolledAt !== undefined && Date.now() - scrolledAt < OWN_SCROLL_WINDOW_MS
|
|
}
|
|
}
|
|
}
|