mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-12 16:02:56 +00:00
fix: session pipeline tools survive another tab taking the panel, reopening the editor on call
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0b4fafc02e
commit
46ec2995cc
@@ -507,6 +507,11 @@ export class AIChatManager implements ChatViewHost {
|
||||
// awaits this so the model's next build_pipeline_node call can't race ahead of
|
||||
// the async canvas mount and hit "Unknown tool call".
|
||||
#pipelineHelpersWaiters = new Set<(helpers: PipelineAIChatHelpers) => void>()
|
||||
// A session's pipeline editor unmounts whenever another tab takes the preview
|
||||
// panel. Its tools stay offered for every folder the session opened, and a call
|
||||
// reopens the tab through this, so a page shown in between costs no failed call.
|
||||
#pipelineReopener: ((folder: string) => void) | undefined = undefined
|
||||
#openedPipelineFolders = new Set<string>()
|
||||
readonly isOpen = $derived(chatState.size > 0)
|
||||
savedSize = $state<number>(0)
|
||||
instructions = $state<string>('')
|
||||
@@ -2504,8 +2509,27 @@ export class AIChatManager implements ChatViewHost {
|
||||
|
||||
// Public because it is purely local, unlike `changeMode(GLOBAL)`, which also
|
||||
// fires the three network refreshes.
|
||||
setPipelineReopener = (reopen: (folder: string) => void) => {
|
||||
this.#pipelineReopener = reopen
|
||||
}
|
||||
|
||||
#pipelineFolders = (): string[] => [
|
||||
...new Set([
|
||||
...[...this.#pipelineEditors].map((p) => p.getFolder()),
|
||||
...(this.#pipelineReopener ? this.#openedPipelineFolders : [])
|
||||
])
|
||||
]
|
||||
|
||||
#ensurePipeline = async (folder: string): Promise<PipelineAIChatHelpers | undefined> => {
|
||||
const find = () => [...this.#pipelineEditors].find((p) => p.getFolder() === folder)
|
||||
const live = find()
|
||||
if (live || !this.#pipelineReopener) return live
|
||||
this.#pipelineReopener(folder)
|
||||
return (await this.waitForPipelineHelpers(folder)) ? find() : undefined
|
||||
}
|
||||
|
||||
configureGlobalMode = () => {
|
||||
const hasPipelines = this.#pipelineEditors.size > 0
|
||||
const hasPipelines = this.#pipelineFolders().length > 0
|
||||
const opts = this.globalAssemblyOpts()
|
||||
const baseHelpers: GlobalToolHelpers = {
|
||||
// A session targets its own fixed (possibly forked) workspace, so capture it for
|
||||
@@ -2538,7 +2562,12 @@ export class AIChatManager implements ChatViewHost {
|
||||
}
|
||||
this.#assembledTools = assembleGlobalTools(opts)
|
||||
this.helpers = hasPipelines
|
||||
? { ...baseHelpers, pipelines: () => [...this.#pipelineEditors] }
|
||||
? {
|
||||
...baseHelpers,
|
||||
pipelines: () => [...this.#pipelineEditors],
|
||||
pipelineFolders: this.#pipelineFolders,
|
||||
ensurePipeline: this.#ensurePipeline
|
||||
}
|
||||
: baseHelpers
|
||||
this.systemMessage = assembleGlobalSystemMessage(getCustomPromptParts(AIMode.GLOBAL), opts)
|
||||
this.syncArtifactsSession()
|
||||
@@ -2552,7 +2581,7 @@ export class AIChatManager implements ChatViewHost {
|
||||
mcpServers: this.mcpServers,
|
||||
access: this.sessionAccess,
|
||||
sessionContext: this.sessionContextResolver?.(),
|
||||
pipelineFolders: [...new Set([...this.#pipelineEditors].map((p) => p.getFolder()))]
|
||||
pipelineFolders: this.#pipelineFolders()
|
||||
})
|
||||
|
||||
// Fetch the workspace's AI skills and, if GLOBAL mode is still active, rebuild
|
||||
@@ -4885,6 +4914,7 @@ export class AIChatManager implements ChatViewHost {
|
||||
// Returns a cleanup that tears the registration back down.
|
||||
setPipelineHelpers = (pipelineHelpers: PipelineAIChatHelpers) => {
|
||||
this.#pipelineEditors.add(pipelineHelpers)
|
||||
this.#openedPipelineFolders.add(pipelineHelpers.getFolder())
|
||||
untrack(() => {
|
||||
if (this.mode === AIMode.GLOBAL) {
|
||||
this.configureGlobalMode()
|
||||
|
||||
@@ -1477,7 +1477,7 @@ ${pipelineBullet}`
|
||||
canWriteDraft,
|
||||
`
|
||||
- After writing or substantially editing a script / flow / app draft, show it via open_preview(kind, path) so the user sees the editor and live preview right next to the chat. First check whether it is already shown: if unsure, call get_preview_status. Only call open_preview (or offer to) when no preview is open or it is showing a different item — don't re-open a preview already showing the item you just edited.
|
||||
- Building a data pipeline: call open_preview(kind="pipeline", path="<folder>") as the FIRST step, before creating any node — this opens the pipeline editor the user reviews in. path is the folder, not an item; an empty ${when(canCreateFolder, 'or not-yet-created ')}folder is fine${when(canCreateFolder, ' (create_folder first if needed, then open it)')}. Opening it adds build_pipeline_node / edit_pipeline_node to your tools from the next step on — they are not in your tool list before open_preview returns, so call open_preview on its own, wait for its result, then build. Use ONLY those to add or change pipeline nodes, never write_script for a pipeline node — they apply directly as unsaved drafts on the canvas (no separate accept/reject step) that the user reviews and deploys. Do not write pipeline scripts without first opening the editor.`
|
||||
- Building a data pipeline: call open_preview(kind="pipeline", path="<folder>") as the FIRST step, before creating any node — this opens the pipeline editor the user reviews in. path is the folder, not an item; an empty ${when(canCreateFolder, 'or not-yet-created ')}folder is fine${when(canCreateFolder, ' (create_folder first if needed, then open it)')}. Opening it adds build_pipeline_node / edit_pipeline_node to your tools from the next step on — they are not in your tool list before open_preview returns, so call open_preview on its own, wait for its result, then build. They then stay available for the rest of the session even while another tab (a page, another preview) is shown: a call brings the pipeline editor back by itself, so do not re-open it first. Use ONLY those to add or change pipeline nodes, never write_script for a pipeline node — they apply directly as unsaved drafts on the canvas (no separate accept/reject step) that the user reviews and deploys. Do not write pipeline scripts without first opening the editor.`
|
||||
)}
|
||||
- When debugging a running raw app, call get_app_runtime_logs to read the live preview's browser console output. It needs the raw app preview open (open_preview kind="raw_app").
|
||||
- Writing an app file does not compile it: the open preview rebuilds it afterwards. After editing a raw app's frontend files with its preview open, call get_app_runtime_logs to check the build — when it failed, it returns the build errors (e.g. syntax or import errors) and bundler logs to fix.
|
||||
|
||||
@@ -634,6 +634,10 @@ function createRuntime(session: Session): SessionRuntime {
|
||||
waiters.add(notify)
|
||||
})
|
||||
}
|
||||
manager.setPipelineReopener((folder) => {
|
||||
const target = previewTargetForSessionTarget('pipeline', folder)
|
||||
if (target) previewTabs.open(target)
|
||||
})
|
||||
async function deployPipeline(folder: string): Promise<DeployResult> {
|
||||
const key = normalizePipelineFolder(folder)
|
||||
const target = previewTargetForSessionTarget('pipeline', key)
|
||||
|
||||
Reference in New Issue
Block a user