fix: take a trigger's flow kind from its config and name the written trigger

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Diego Imbert
2026-09-17 15:50:04 +02:00
co-authored by Claude Opus 5
parent 9d029c0d44
commit 29abd63de6
15 changed files with 53 additions and 14 deletions
@@ -34,8 +34,8 @@
title
}: Props = $props()
// The drawers ride with the docked chat, so a surface can render this pill with nothing
// able to open one.
// The drawers ride with the docked chat and the session tabs with the sessions page, so a
// surface can render this pill with nothing able to open one.
const available = $derived.by(() => {
const action = workspaceItemAction(wmKind, wmPath, wmTargetKind, wmRawApp === 'true')
return action && hasToolDisplayActionHandler(action.type) ? action : undefined
@@ -113,8 +113,8 @@
variant="subtle"
iconOnly
startIcon={{ icon: PanelRight }}
title="Open in drawer"
aria-label="Open {wmPath} in drawer"
title="Open in editor"
aria-label="Open {wmPath} in editor"
wrapperClasses="ml-0.5 inline-flex self-center shrink-0 opacity-0 group-hover:opacity-100 transition-opacity"
btnClasses="!w-auto !rounded !p-0.5 !text-tertiary"
onClick={openDrawer}
@@ -125,8 +125,9 @@
<div class="truncate text-xs font-semibold text-primary">{card.title}</div>
<div class="truncate text-2xs text-secondary">{card.subtitle}</div>
</div>
<!-- open_created_resource is serviced solely by the docked chat's drawers, so
elsewhere the card stands alone as a record of what the tool created. -->
<!-- open_created_resource is serviced only by the docked chat's drawers and the
sessions page's tabs, so elsewhere the card stands alone as a record of what the
tool created. -->
{#if hasToolDisplayActionHandler(action.type)}
<Button
unifiedSize="sm"
@@ -1,6 +1,7 @@
import { describe, it, expect } from 'vitest'
import { toolReloadEffect, tabsToReload } from './previewReload'
import type { SessionPreviewTab } from './sessionState.svelte'
import { pageItemUrl } from './previewPaths'
describe('toolReloadEffect', () => {
it('maps a non-item mutation to its own list page only', () => {
@@ -52,6 +53,26 @@ describe('toolReloadEffect', () => {
})
})
describe('page item tabs', () => {
const tab = (url: string): SessionPreviewTab => ({ id: url, url, loc: url })
const kafkaA = tab('pageitem:trigger.kafka/u%2Fme%2Fa')
const kafkaB = tab('pageitem:trigger.kafka/u%2Fme%2Fb')
const list = tab('/kafka_triggers')
it('reloads only the trigger a write names, and its list page', () => {
const { pages, items } = toolReloadEffect('write_trigger', {
kind: 'kafka',
config: { path: 'u/me/a' }
})
const named = new Set(items.map((i) => pageItemUrl(i)))
expect(tabsToReload([kafkaA, kafkaB, list], new Set(pages), named)).toEqual([kafkaA, list])
})
it('reloads every tab of the kind when the tool names no item', () => {
expect(tabsToReload([kafkaA, kafkaB], new Set(['/kafka_triggers']))).toEqual([kafkaA, kafkaB])
})
})
describe('tabsToReload', () => {
const scheduleTab: SessionPreviewTab = { id: 's', url: '/schedules', loc: '/schedules' }
const resourceTab: SessionPreviewTab = { id: 'r', url: '/resources', loc: '/resources' }
@@ -34,7 +34,8 @@ export function toolReloadEffect(name: string, args: any): ToolReloadEffect {
case 'write_schedule':
return withItem(['/schedules'], itemRef('schedule', args))
case 'write_trigger':
return withItem(triggerPages(args?.kind), itemRef('trigger', args, args?.kind))
// Its path sits in the trigger's own config, not beside `kind`.
return withItem(triggerPages(args?.kind), itemRef('trigger', args?.config, args?.kind))
case 'write_resource':
return withItem(['/resources'], itemRef('resource', args))
case 'write_variable':
@@ -234,6 +234,8 @@
}
async function loadTriggerConfig(cfg?: Record<string, any>): Promise<void> {
// The loaded trigger says what it runs; an opener's `isFlow` is only its guess.
if (cfg?.is_flow !== undefined) itemKind = cfg.is_flow ? 'flow' : 'script'
try {
amqp_resource_path = cfg?.amqp_resource_path
queue_name = cfg?.queue_name ?? ''
@@ -232,6 +232,8 @@
}
async function loadTriggerConfig(cfg?: Record<string, any>): Promise<void> {
// The loaded trigger says what it runs; an opener's `isFlow` is only its guess.
if (cfg?.is_flow !== undefined) itemKind = cfg.is_flow ? 'flow' : 'script'
script_path = cfg?.script_path
initialScriptPath = cfg?.script_path
azure_resource_path = cfg?.azure_resource_path
@@ -257,13 +259,7 @@
const previousPath = initialPath
const cfg = azureConfig
if (!cfg) return
const isSaved = await saveAzureTriggerFromCfg(
initialPath,
cfg,
edit,
wsId!,
usedTriggerKinds
)
const isSaved = await saveAzureTriggerFromCfg(initialPath, cfg, edit, wsId!, usedTriggerKinds)
if (isSaved) {
draftSync.discard(previousPath, getAzureConfig())
onUpdate?.(cfg.path)
@@ -209,6 +209,8 @@
}
function loadTriggerConfig(cfg?: Partial<EmailTrigger>): void {
// The loaded trigger says what it runs; an opener's `isFlow` is only its guess.
if (cfg?.is_flow !== undefined) itemKind = cfg.is_flow ? 'flow' : 'script'
script_path = cfg?.script_path ?? ''
initialScriptPath = cfg?.script_path ?? ''
is_flow = cfg?.is_flow ?? false
@@ -259,6 +259,8 @@
}
async function loadTriggerConfig(cfg?: Record<string, any>): Promise<void> {
// The loaded trigger says what it runs; an opener's `isFlow` is only its guess.
if (cfg?.is_flow !== undefined) itemKind = cfg.is_flow ? 'flow' : 'script'
script_path = cfg?.script_path
initialScriptPath = cfg?.script_path
gcp_resource_path = cfg?.gcp_resource_path ?? undefined
@@ -352,6 +352,8 @@
}
function loadTriggerConfig(cfg?: Partial<HttpTrigger>): void {
// The loaded trigger says what it runs; an opener's `isFlow` is only its guess.
if (cfg?.is_flow !== undefined) itemKind = cfg.is_flow ? 'flow' : 'script'
script_path = cfg?.script_path ?? ''
initialScriptPath = cfg?.script_path ?? ''
is_flow = cfg?.is_flow ?? false
@@ -241,6 +241,8 @@
}
function loadTriggerConfig(cfg?: Record<string, any>): void {
// The loaded trigger says what it runs; an opener's `isFlow` is only its guess.
if (cfg?.is_flow !== undefined) itemKind = cfg.is_flow ? 'flow' : 'script'
script_path = cfg?.script_path
initialScriptPath = cfg?.script_path
is_flow = cfg?.is_flow
@@ -237,6 +237,8 @@
}
async function loadTriggerConfig(cfg?: Record<string, any>): Promise<void> {
// The loaded trigger says what it runs; an opener's `isFlow` is only its guess.
if (cfg?.is_flow !== undefined) itemKind = cfg.is_flow ? 'flow' : 'script'
try {
mqtt_resource_path = cfg?.mqtt_resource_path
subscribe_topics = cfg?.subscribe_topics
@@ -230,6 +230,8 @@
}
async function loadTriggerConfig(cfg?: Record<string, any>): Promise<void> {
// The loaded trigger says what it runs; an opener's `isFlow` is only its guess.
if (cfg?.is_flow !== undefined) itemKind = cfg.is_flow ? 'flow' : 'script'
script_path = cfg?.script_path
initialScriptPath = cfg?.script_path
is_flow = cfg?.is_flow
@@ -366,6 +366,8 @@
}
async function loadTriggerConfig(cfg?: Record<string, any>): Promise<void> {
// The loaded trigger says what it runs; an opener's `isFlow` is only its guess.
if (cfg?.is_flow !== undefined) itemKind = cfg.is_flow ? 'flow' : 'script'
script_path = cfg?.script_path
initialScriptPath = cfg?.script_path
is_flow = cfg?.is_flow
@@ -219,6 +219,8 @@
}
async function loadTriggerConfig(cfg?: Record<string, any>): Promise<void> {
// The loaded trigger says what it runs; an opener's `isFlow` is only its guess.
if (cfg?.is_flow !== undefined) itemKind = cfg.is_flow ? 'flow' : 'script'
try {
script_path = cfg?.script_path
initialScriptPath = cfg?.script_path
@@ -264,6 +264,8 @@
}
function loadTriggerConfig(cfg?: Record<string, any>): void {
// The loaded trigger says what it runs; an opener's `isFlow` is only its guess.
if (cfg?.is_flow !== undefined) itemKind = cfg.is_flow ? 'flow' : 'script'
script_path = cfg?.script_path
initialScriptPath = cfg?.script_path
is_flow = cfg?.is_flow