diff --git a/frontend/src/lib/components/flows/agentDraft.svelte.ts b/frontend/src/lib/components/flows/agentDraft.svelte.ts index 0499fbfd9b..fd87b950de 100644 --- a/frontend/src/lib/components/flows/agentDraft.svelte.ts +++ b/frontend/src/lib/components/flows/agentDraft.svelte.ts @@ -277,6 +277,11 @@ export function useAgentDraft(opts: AgentDraftOptions): AgentDraftHandle { async ([r, user]) => { // A slower response for a path we have left must not overwrite the current one. if (loadedFor !== key) return + // The path was minted as free, so something already there is someone else's work. + if (opts.isNew?.()) { + refuse(`${path} already exists. Close this and create the agent again.`) + return + } // A step's `agent` is caller-authored, so it can name a resource of any type, and a // deploy from here would replace that resource's whole value while keeping its type. const refused = agentEditorRefusal(path, r.resource_type) diff --git a/frontend/src/lib/components/flows/content/AgentEditorModal.svelte b/frontend/src/lib/components/flows/content/AgentEditorModal.svelte index e01bdc777e..f9f367e0ef 100644 --- a/frontend/src/lib/components/flows/content/AgentEditorModal.svelte +++ b/frontend/src/lib/components/flows/content/AgentEditorModal.svelte @@ -218,7 +218,9 @@ const at = deployingFor ?? currentWriteTarget() // Before the rename is announced: it finds the steps to refresh under the old path. const reconciled = reconcile(at, savedPath) - if (at && savedPath !== at.path) { + // A new agent's first deploy is announced like a rename even at the same path: the surface + // has anchored nothing for it yet, and reopening drops `isNew` now that it exists. + if (at && (savedPath !== at.path || target?.isNew)) { onRenamed?.(at.path, savedPath) // The dialog is keyed on the path, so this reloads it on the renamed agent. Only while it // still shows the one deployed: it can be closed or pointed elsewhere mid-request. diff --git a/frontend/src/lib/components/home/ItemsList.svelte b/frontend/src/lib/components/home/ItemsList.svelte index de65860e5e..b27b32097f 100644 --- a/frontend/src/lib/components/home/ItemsList.svelte +++ b/frontend/src/lib/components/home/ItemsList.svelte @@ -445,6 +445,8 @@ // nothing to append for agents. if (reset) void loadAgents() if (!showsRunnables) { + // Supersedes a page still in flight, which would otherwise land in the agent view. + ++loadGen scripts = [] flows = [] apps = [] @@ -628,6 +630,13 @@ // Track the prefix as open first — even a no-op call (re-expanding a cached node) // means it's on screen, so later reloads must refresh it. openOwners.add(owner) + // The agent view has no runnables to page in: its rows are the loaded agents, which + // `treeSource` adds itself. Rows an owner held from another kind go with the switch. + if (!showsRunnables) { + treeOwnerItems = treeOwnerItems.filter((x) => !effectivePath(x).startsWith(`${owner}/`)) + ownerLoad[owner] = { hasMore: false, loading: false, loaded: true, gen: treeGen } + return + } const st = ownerLoad[owner] // Only a load for the CURRENT generation blocks a new one. A load left in flight // by a superseded generation (treeGen bumped on a sort/filter reload) has already