@@ -128,28 +128,33 @@ describe("outbound channel resolution", () => {
128128 ) . toBe ( plugin ) ;
129129 } ) ;
130130
131- it ( "does not load registries while resolving outbound plugins " , async ( ) => {
131+ it ( "bootstraps configured channel plugins when the active registry is missing the target " , async ( ) => {
132132 const plugin = { id : "alpha" } ;
133133 getLoadedChannelPluginMock . mockReturnValueOnce ( undefined ) . mockReturnValueOnce ( plugin ) ;
134- const channelResolution = await importChannelResolution ( "no- bootstrap" ) ;
134+ const channelResolution = await importChannelResolution ( "bootstrap-missing-target " ) ;
135135
136136 expect (
137137 channelResolution . resolveOutboundChannelPlugin ( {
138138 channel : "alpha" ,
139139 cfg : { channels : { } } as never ,
140+ allowBootstrap : true ,
140141 } ) ,
141142 ) . toBe ( plugin ) ;
142- expect ( resolveRuntimePluginRegistryMock ) . not . toHaveBeenCalled ( ) ;
143-
144- getChannelPluginMock . mockReturnValue ( undefined ) ;
145- channelResolution . resolveOutboundChannelPlugin ( {
146- channel : "alpha" ,
147- cfg : { channels : { } } as never ,
148- } ) ;
149- expect ( resolveRuntimePluginRegistryMock ) . not . toHaveBeenCalled ( ) ;
143+ expect ( applyPluginAutoEnableMock ) . toHaveBeenCalledWith ( { config : { channels : { } } } ) ;
144+ expect ( resolveRuntimePluginRegistryMock ) . toHaveBeenCalledWith (
145+ expect . objectContaining ( {
146+ config : { autoEnabled : true } ,
147+ activationSourceConfig : { channels : { } } ,
148+ autoEnabledReasons : { } ,
149+ workspaceDir : "/tmp/workspace" ,
150+ runtimeOptions : {
151+ allowGatewaySubagentBinding : true ,
152+ } ,
153+ } ) ,
154+ ) ;
150155 } ) ;
151156
152- it ( "does not load when the active registry has other channels but not the requested one" , async ( ) => {
157+ it ( "attempts activation when the active registry has other channels but not the requested one" , async ( ) => {
153158 getLoadedChannelPluginMock . mockReturnValue ( undefined ) ;
154159 getChannelPluginMock . mockReturnValue ( undefined ) ;
155160 getActivePluginRegistryMock . mockReturnValue ( {
@@ -164,47 +169,80 @@ describe("outbound channel resolution", () => {
164169 channelResolution . resolveOutboundChannelPlugin ( {
165170 channel : "alpha" ,
166171 cfg : { channels : { } } as never ,
172+ allowBootstrap : true ,
167173 } ) ,
168174 ) . toBeUndefined ( ) ;
169- expect ( resolveRuntimePluginRegistryMock ) . not . toHaveBeenCalled ( ) ;
175+ expect ( resolveRuntimePluginRegistryMock ) . toHaveBeenCalledTimes ( 1 ) ;
170176 } ) ;
171177
172178 it ( "does not retry registry loads after a missing outbound plugin" , async ( ) => {
173179 getChannelPluginMock . mockReturnValue ( undefined ) ;
174- resolveRuntimePluginRegistryMock . mockImplementationOnce ( ( ) => {
175- throw new Error ( "transient" ) ;
176- } ) ;
177180 const channelResolution = await importChannelResolution ( "bootstrap-retry" ) ;
178181
179182 expect (
180183 channelResolution . resolveOutboundChannelPlugin ( {
181184 channel : "alpha" ,
182185 cfg : { channels : { } } as never ,
186+ allowBootstrap : true ,
183187 } ) ,
184188 ) . toBeUndefined ( ) ;
185189
186190 channelResolution . resolveOutboundChannelPlugin ( {
187191 channel : "alpha" ,
188192 cfg : { channels : { } } as never ,
193+ allowBootstrap : true ,
189194 } ) ;
190- expect ( resolveRuntimePluginRegistryMock ) . not . toHaveBeenCalled ( ) ;
195+ expect ( resolveRuntimePluginRegistryMock ) . toHaveBeenCalledTimes ( 1 ) ;
191196 } ) ;
192197
193- it ( "does not load when the pinned channel registry version changes" , async ( ) => {
198+ it ( "allows another activation attempt when the pinned channel registry version changes" , async ( ) => {
194199 getChannelPluginMock . mockReturnValue ( undefined ) ;
195200 const channelResolution = await importChannelResolution ( "channel-version-change" ) ;
196201
197202 channelResolution . resolveOutboundChannelPlugin ( {
198203 channel : "alpha" ,
199204 cfg : { channels : { } } as never ,
205+ allowBootstrap : true ,
200206 } ) ;
201- expect ( resolveRuntimePluginRegistryMock ) . not . toHaveBeenCalled ( ) ;
207+ expect ( resolveRuntimePluginRegistryMock ) . toHaveBeenCalledTimes ( 1 ) ;
202208
203209 getActivePluginChannelRegistryVersionMock . mockReturnValue ( 2 ) ;
204210 channelResolution . resolveOutboundChannelPlugin ( {
205211 channel : "alpha" ,
206212 cfg : { channels : { } } as never ,
213+ allowBootstrap : true ,
207214 } ) ;
215+ expect ( resolveRuntimePluginRegistryMock ) . toHaveBeenCalledTimes ( 2 ) ;
216+ } ) ;
217+
218+ it ( "resolves message adapters through the activation-aware channel plugin path" , async ( ) => {
219+ const message = { send : { text : vi . fn ( ) } } ;
220+ const plugin = { id : "alpha" , message } ;
221+ getLoadedChannelPluginMock . mockReturnValueOnce ( undefined ) . mockReturnValueOnce ( plugin ) ;
222+ const channelResolution = await importChannelResolution ( "message-adapter-bootstrap" ) ;
223+
224+ expect (
225+ channelResolution . resolveOutboundChannelMessageAdapter ( {
226+ channel : "alpha" ,
227+ cfg : { channels : { } } as never ,
228+ allowBootstrap : true ,
229+ } ) ,
230+ ) . toBe ( message ) ;
231+ expect ( resolveRuntimePluginRegistryMock ) . toHaveBeenCalledTimes ( 1 ) ;
232+ } ) ;
233+
234+ it ( "does not bootstrap by default for outbound hot-path resolution" , async ( ) => {
235+ const plugin = { id : "alpha" } ;
236+ getLoadedChannelPluginMock . mockReturnValue ( undefined ) ;
237+ getChannelPluginMock . mockReturnValue ( plugin ) ;
238+ const channelResolution = await importChannelResolution ( "no-bootstrap-default" ) ;
239+
240+ expect (
241+ channelResolution . resolveOutboundChannelPlugin ( {
242+ channel : "alpha" ,
243+ cfg : { channels : { } } as never ,
244+ } ) ,
245+ ) . toBe ( plugin ) ;
208246 expect ( resolveRuntimePluginRegistryMock ) . not . toHaveBeenCalled ( ) ;
209247 } ) ;
210248} ) ;
0 commit comments