@@ -26,6 +26,7 @@ import {
2626 buildSubBlockValues ,
2727 type CanonicalModeOverrides ,
2828 evaluateSubBlockCondition ,
29+ getCanonicalSubBlocksForSurface ,
2930 isCanonicalPair ,
3031 isNonEmptyValue ,
3132 reindexCanonicalModesByPosition ,
@@ -228,7 +229,9 @@ export type SubBlockTransform = (
228229 subBlocks : SubBlockRecord ,
229230 blockType : string ,
230231 canonicalModes ?: CanonicalModeOverrides ,
231- onCanonicalModesChanged ?: ( next : CanonicalModeOverrides ) => void
232+ onCanonicalModesChanged ?: ( next : CanonicalModeOverrides ) => void ,
233+ /** The block's trigger mode, scoping the canonical index (see {@link createCanonicalModeGates}). */
234+ triggerMode ?: boolean
232235) => SubBlockRecord
233236
234237/**
@@ -451,16 +454,26 @@ const NO_GATES: CanonicalModeGates = {
451454 * augmented with each pair's ACTIVE value under its canonical id, mirroring how the serializer
452455 * exposes params to conditions. With no configs (unknown block type) every gate is a no-op:
453456 * everything is detected and nothing passes through, the conservative default.
457+ *
458+ * `triggerSurface` scopes the index to the block's active surface. Without it, a trigger field
459+ * sharing a `canonicalParamId` with an action pair (`triggerSiteId` under `siteId`,
460+ * `triggerCredentials` under `oauthCredential`) is read as a member of THAT pair, and since it is
461+ * neither its `basicId` nor in its `advancedIds`, `isDormantMember` answers `true` the moment the
462+ * shared mode resolves to advanced — which a fork acts on by CLEARING the value. Pass a caller
463+ * that has already narrowed its configs (the dependent scan) `false`; scoping twice is harmless
464+ * but the flag should describe what the caller actually did.
454465 */
455466export function createCanonicalModeGates (
456467 configSubBlocks : SubBlockConfig [ ] | undefined ,
457468 values : Record < string , unknown > ,
458- canonicalModes ?: CanonicalModeOverrides
469+ canonicalModes ?: CanonicalModeOverrides ,
470+ triggerSurface = false
459471) : CanonicalModeGates {
460472 if ( ! configSubBlocks || configSubBlocks . length === 0 ) return NO_GATES
461- const canonicalIndex = buildCanonicalIndex ( configSubBlocks )
473+ const surfaceSubBlocks = getCanonicalSubBlocksForSurface ( configSubBlocks , triggerSurface )
474+ const canonicalIndex = buildCanonicalIndex ( surfaceSubBlocks )
462475 const configByBaseKey = new Map (
463- configSubBlocks . filter ( ( cfg ) => cfg . id ) . map ( ( cfg ) => [ cfg . id , cfg ] )
476+ surfaceSubBlocks . filter ( ( cfg ) => cfg . id ) . map ( ( cfg ) => [ cfg . id , cfg ] )
464477 )
465478 const conditionValues = { ...values }
466479 for ( const [ canonicalId , group ] of Object . entries ( canonicalIndex . groupsById ) ) {
@@ -521,6 +534,12 @@ export interface RemapForkContext {
521534 blockType ?: string
522535 /** Canonical-mode overrides (`block.data.canonicalModes`), picking the active member per pair. */
523536 canonicalModes ?: CanonicalModeOverrides
537+ /**
538+ * Whether the block is in TRIGGER mode, scoping the canonical index to that surface. A mixed
539+ * action/trigger block shares one mode key across both surfaces, so without this a trigger
540+ * field reads as a dormant member of the action pair and its value is cleared.
541+ */
542+ triggerMode ?: boolean
524543 /** Target MCP server row lookup for rewriting remapped tool-input entries' server metadata. */
525544 resolveMcpServerMeta ?: ForkMcpServerMetaResolver
526545 /**
@@ -1047,7 +1066,8 @@ export function remapForkSubBlocks(
10471066 const gates = createCanonicalModeGates (
10481067 context ?. blockType ? getBlock ( context . blockType ) ?. subBlocks : undefined ,
10491068 buildSubBlockValues ( subBlocks ) ,
1050- context ?. canonicalModes
1069+ context ?. canonicalModes ,
1070+ context ?. triggerMode === true
10511071 )
10521072
10531073 for ( const [ subBlockKey , subBlock ] of Object . entries ( subBlocks ) ) {
@@ -1276,7 +1296,9 @@ export function clearDependentsOnRemap(
12761296 remappedKeys : ReadonlySet < string > ,
12771297 canonicalModes ?: CanonicalModeOverrides ,
12781298 /** Keys remapped via a COPY (see {@link RemapSubBlocksResult.copyRemappedKeys}). */
1279- copyRemappedKeys ?: ReadonlySet < string >
1299+ copyRemappedKeys ?: ReadonlySet < string > ,
1300+ /** The block's trigger mode, scoping the canonical index (see {@link createCanonicalModeGates}). */
1301+ triggerMode ?: boolean
12801302) : SubBlockRecord {
12811303 if ( remappedKeys . size === 0 ) return subBlocks
12821304 const config = getBlock ( blockType )
@@ -1290,7 +1312,8 @@ export function clearDependentsOnRemap(
12901312 const gates = createCanonicalModeGates (
12911313 config . subBlocks ,
12921314 buildSubBlockValues ( subBlocks ) ,
1293- canonicalModes
1315+ canonicalModes ,
1316+ triggerMode === true
12941317 )
12951318
12961319 // The exemption's parent test: an mcp-server selector whose POST-remap value is non-empty was
@@ -1498,15 +1521,22 @@ export function collectClearedDependents(
14981521 blockName : string ,
14991522 targetCurrentSubBlocks : SubBlockRecord ,
15001523 mergedSubBlocks : SubBlockRecord ,
1501- canonicalModes ?: CanonicalModeOverrides
1524+ canonicalModes ?: CanonicalModeOverrides ,
1525+ /** The block's trigger mode, scoping the canonical index (see {@link createCanonicalModeGates}). */
1526+ triggerMode ?: boolean
15021527) : NeedsConfigurationField [ ] {
15031528 const config = getBlock ( blockType )
15041529 if ( ! config ) return [ ]
15051530 const targetValues = buildSubBlockValues ( targetCurrentSubBlocks )
15061531 const mergedValues = buildSubBlockValues ( mergedSubBlocks )
15071532 // A DORMANT canonical member the merge cleared is not a lost configuration - only the pair's
15081533 // active member executes, so an inactive slot must never demand a re-pick.
1509- const gates = createCanonicalModeGates ( config . subBlocks , mergedValues , canonicalModes )
1534+ const gates = createCanonicalModeGates (
1535+ config . subBlocks ,
1536+ mergedValues ,
1537+ canonicalModes ,
1538+ triggerMode === true
1539+ )
15101540 const fields : NeedsConfigurationField [ ] = [ ]
15111541 for ( const cfg of config . subBlocks ) {
15121542 if ( ! cfg . id ) continue
@@ -1755,10 +1785,11 @@ export function createForkSubBlockTransform(
17551785 isCopiedTarget ?: ( kind : ForkRemapKind , sourceId : string ) => boolean
17561786 }
17571787) : SubBlockTransform {
1758- return ( subBlocks , blockType , canonicalModes , onCanonicalModesChanged ) => {
1788+ return ( subBlocks , blockType , canonicalModes , onCanonicalModesChanged , triggerMode ) => {
17591789 const result = remapSubBlocks ( subBlocks , resolve , {
17601790 blockType,
17611791 canonicalModes,
1792+ triggerMode,
17621793 resolveMcpServerMeta : options ?. resolveMcpServerMeta ,
17631794 isCopiedTarget : options ?. isCopiedTarget ,
17641795 } )
@@ -1768,7 +1799,8 @@ export function createForkSubBlockTransform(
17681799 blockType ,
17691800 result . remappedKeys ,
17701801 result . canonicalModes ?? canonicalModes ,
1771- result . copyRemappedKeys
1802+ result . copyRemappedKeys ,
1803+ triggerMode
17721804 )
17731805 }
17741806}
@@ -1792,6 +1824,8 @@ export function scanWorkflowReferences(
17921824 subBlocks : unknown
17931825 /** `block.data.canonicalModes`, picking the active member per canonical pair for detection. */
17941826 canonicalModes ?: CanonicalModeOverrides
1827+ /** The block's trigger mode, scoping the canonical index (see {@link createCanonicalModeGates}). */
1828+ triggerMode ?: boolean
17951829 } > ,
17961830 resolve : ForkReferenceResolver
17971831) : WorkflowReferenceScan {
@@ -1822,6 +1856,7 @@ export function scanWorkflowReferences(
18221856 blockName : block . name ,
18231857 blockType : block . type ,
18241858 canonicalModes : block . canonicalModes ,
1859+ triggerMode : block . triggerMode ,
18251860 } )
18261861 for ( const reference of blockResult . references ) {
18271862 const key = `${ reference . kind } :${ reference . sourceId } `
0 commit comments