@@ -190,6 +190,74 @@ describe("trusted-catalog load-path discovery", () => {
190190 } ) ;
191191 } ) ;
192192
193+ it ( "keeps origin-specific fallback when local and bundled entries share a plugin id" , ( ) => {
194+ getChannelPluginCatalogEntry . mockImplementation (
195+ (
196+ _channelId : string ,
197+ options ?: {
198+ excludePluginRefs ?: Array < { pluginId : string ; origin ?: string } > ;
199+ workspaceDir ?: string ;
200+ } ,
201+ ) => {
202+ if (
203+ options ?. excludePluginRefs ?. some (
204+ ( entry ) => entry . pluginId === "telegram" && entry . origin === "config" ,
205+ )
206+ ) {
207+ return {
208+ id : "telegram" ,
209+ pluginId : "telegram" ,
210+ origin : "bundled" ,
211+ meta : {
212+ id : "telegram" ,
213+ label : "Telegram" ,
214+ selectionLabel : "Telegram" ,
215+ docsPath : "/channels/telegram" ,
216+ blurb : "bundled entry" ,
217+ } ,
218+ install : { localPath : "./bundled/telegram" , defaultChoice : "local" } ,
219+ } ;
220+ }
221+ return {
222+ id : "telegram" ,
223+ pluginId : "telegram" ,
224+ origin : "config" ,
225+ meta : {
226+ id : "telegram" ,
227+ label : "Telegram Shadow" ,
228+ selectionLabel : "Telegram Shadow" ,
229+ docsPath : "/channels/telegram" ,
230+ blurb : "config shadow" ,
231+ } ,
232+ install : { localPath : "./plugins/telegram-shadow" , defaultChoice : "local" } ,
233+ } ;
234+ } ,
235+ ) ;
236+
237+ expect (
238+ getTrustedChannelPluginCatalogEntry ( "telegram" , {
239+ cfg : {
240+ plugins : {
241+ load : {
242+ paths : [ "/tmp/load-path-a" ] ,
243+ } ,
244+ } ,
245+ } ,
246+ workspaceDir : "/tmp/workspace" ,
247+ } ) ,
248+ ) . toMatchObject ( {
249+ id : "telegram" ,
250+ pluginId : "telegram" ,
251+ origin : "bundled" ,
252+ } ) ;
253+ expect ( getChannelPluginCatalogEntry ) . toHaveBeenNthCalledWith ( 2 , "telegram" , {
254+ excludePluginRefs : [ { pluginId : "telegram" , origin : "config" } ] ,
255+ env : undefined ,
256+ extraPaths : [ "/tmp/load-path-a" ] ,
257+ workspaceDir : "/tmp/workspace" ,
258+ } ) ;
259+ } ) ;
260+
193261 it ( "stops when fallback lookup resurfaces the same untrusted local entry" , ( ) => {
194262 getChannelPluginCatalogEntry . mockReturnValue ( {
195263 id : "msteams" ,
0 commit comments