@@ -254,6 +254,7 @@ export function policyFromIntent(
254254 options : { persistence ?: SurfacePersistence } = { } ,
255255) : SurfacePolicy {
256256 const persistence = options . persistence ?? 'replayable' ;
257+ const hasSurfaceAccess = intent . requestedCapabilities . length > 0 || intent . requestedComponents . length > 0 ;
257258 if ( intent . sideEffect === 'approval-required' || intent . interaction === 'approval' ) {
258259 return {
259260 tier : 'approval' ,
@@ -273,10 +274,13 @@ export function policyFromIntent(
273274 } ;
274275 }
275276 if (
276- intent . interaction !== 'none' ||
277- intent . dataNeed === 'host-resource' ||
278- intent . sideEffect === 'local-state' ||
279- intent . sideEffect === 'external-action'
277+ hasSurfaceAccess &&
278+ (
279+ intent . interaction !== 'none' ||
280+ intent . dataNeed === 'host-resource' ||
281+ intent . sideEffect === 'local-state' ||
282+ intent . sideEffect === 'external-action'
283+ )
280284 ) {
281285 return {
282286 tier : 'declarative' ,
@@ -587,44 +591,40 @@ function inferCapabilityNames(prompt: string, pack: CapabilityPack | null): stri
587591 const intents = pack ?. intents ?? [ ] ;
588592 if ( intents . length === 0 ) return [ ] ;
589593 const text = prompt . toLowerCase ( ) ;
590- const matches = intents . filter ( ( intent ) => capabilityMatchesIntent ( prompt , text , intent ) ) ;
591- if ( matches . length > 0 ) return matches . map ( ( intent ) => intent . name ) ;
594+ const directMatches = intents . filter ( ( intent ) => matchesCapabilityName ( text , intent . name ) ) ;
595+ if ( directMatches . length > 0 ) return directMatches . map ( ( intent ) => intent . name ) ;
592596
593597 const approval = APPROVAL_RE . test ( prompt )
594598 ? intents . filter ( ( intent ) => intentAuthority ( intent ) === 'approval-gated' )
595599 : [ ] ;
596- if ( approval . length === 1 ) return [ approval [ 0 ] ! . name ] ;
600+ if ( approval . length > 0 ) return singleCandidateNames ( approval ) ;
597601
598602 const worker = BACKGROUND_RE . test ( prompt )
599603 ? intents . filter ( ( intent ) => intentData ( intent ) === 'worker' )
600604 : [ ] ;
601- if ( worker . length > 0 ) return worker . map ( ( intent ) => intent . name ) ;
605+ if ( worker . length > 0 ) return singleCandidateNames ( worker ) ;
602606
603607 const resource = SEARCH_RE . test ( prompt )
604608 ? intents . filter ( ( intent ) => intentData ( intent ) === 'host-resource' )
605609 : [ ] ;
606- if ( resource . length === 1 ) return [ resource [ 0 ] ! . name ] ;
610+ if ( resource . length > 0 ) return singleCandidateNames ( resource ) ;
607611
608- const actions = intents . filter ( ( intent ) => intentAuthority ( intent ) === 'host-action' ) ;
609- if ( ( FORM_RE . test ( prompt ) || SELECT_RE . test ( prompt ) ) && actions . length === 1 ) {
610- return [ actions [ 0 ] ! . name ] ;
612+ const actions = intents . filter ( ( intent ) => capabilityMatchesActionClass ( prompt , intent ) ) ;
613+ if ( actions . length > 0 ) {
614+ return singleCandidateNames ( actions ) ;
611615 }
612616 return [ ] ;
613617}
614618
615- function capabilityMatchesIntent ( prompt : string , text : string , intent : IntentSpec ) : boolean {
616- if ( matchesCapabilityName ( text , intent . name ) ) return true ;
617-
619+ function capabilityMatchesActionClass ( prompt : string , intent : IntentSpec ) : boolean {
618620 const data = intentData ( intent ) ;
619621 const authority = intentAuthority ( intent ) ;
620- if ( APPROVAL_RE . test ( prompt ) ) return authority === 'approval-gated' ;
621- if ( BACKGROUND_RE . test ( prompt ) ) return data === 'worker' ;
622- if ( SEARCH_RE . test ( prompt ) ) return data === 'host-resource' ;
622+ if ( authority !== 'host-action' || data === 'worker' ) return false ;
623623 if ( FORM_RE . test ( prompt ) ) {
624- return authority === 'host-action' && data !== 'worker' && capabilityClassMatches ( intent , FORM_CAPABILITY_RE ) ;
624+ return capabilityClassMatches ( intent , FORM_CAPABILITY_RE ) ;
625625 }
626626 if ( SELECT_RE . test ( prompt ) ) {
627- return authority === 'host-action' && data !== 'worker' && capabilityClassMatches ( intent , SELECT_CAPABILITY_RE ) ;
627+ return capabilityClassMatches ( intent , SELECT_CAPABILITY_RE ) ;
628628 }
629629 return false ;
630630}
@@ -639,6 +639,10 @@ function matchesCapabilityName(text: string, name: string): boolean {
639639 return terms . every ( ( term ) => text . includes ( term ) ) ;
640640}
641641
642+ function singleCandidateNames ( intents : IntentSpec [ ] ) : string [ ] {
643+ return intents . length === 1 ? [ intents [ 0 ] ! . name ] : [ ] ;
644+ }
645+
642646function capabilityClassMatches ( intent : IntentSpec , pattern : RegExp ) : boolean {
643647 return pattern . test ( `${ intent . name } ${ intent . description } ` ) ;
644648}
0 commit comments